mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f59e9ab22 | ||
|
|
515a1a3d78 | ||
|
|
03ffcb1215 | ||
|
|
131098bfdd | ||
|
|
b5277e5f4c | ||
|
|
52f1f7726f | ||
|
|
e19534da47 | ||
|
|
488646e755 | ||
|
|
a8a0024321 | ||
|
|
032deba775 | ||
|
|
7b240a027a | ||
|
|
d4bfe643d5 | ||
|
|
12446a6c44 | ||
|
|
846528ce9c | ||
|
|
7383a9040d | ||
|
|
ab09e7fa14 | ||
|
|
cd3d9b06bb | ||
|
|
fd4628d419 | ||
|
|
5fdfa3132f |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -10,6 +10,7 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# WHEN BUMPING, UPDATE RUBIK IMAGE TO POINT TO VAR INSTEAD OF BEING HARDCODED
|
||||
IMAGE_VERSION: v2026.1.3
|
||||
|
||||
jobs:
|
||||
@@ -560,7 +561,8 @@ jobs:
|
||||
artifact-name: LinuxArm64
|
||||
image_suffix: rubikpi3
|
||||
plat_override: LINUX_QCS6490
|
||||
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_rubikpi3.tar.xz
|
||||
# CHANGE BACK TO $IMAGE_VERSION AFTER BUMPING
|
||||
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2026.1.4/photonvision_rubikpi3.tar.xz
|
||||
minimum_free_mb: 1024
|
||||
root_location: 'offset=569376768'
|
||||
shrink_image: 'no'
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -156,3 +156,8 @@ photon-client/playwright-report/
|
||||
photon-client/blob-report/
|
||||
photon-client/playwright/.cache/
|
||||
photon-client/playwright/.auth/
|
||||
|
||||
# Nix files
|
||||
shell.nix
|
||||
flake.nix
|
||||
flake.lock
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"deviceName" : "Limelight 2+",
|
||||
"supportURL" : "https://limelightvision.io",
|
||||
"ledPins" : [ 13, 18 ],
|
||||
"ledsCanDim" : true,
|
||||
"ledPWMFrequency" : 1000,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"deviceName" : "Limelight 2",
|
||||
"supportURL" : "https://limelightvision.io",
|
||||
"ledPins" : [ 17, 18 ],
|
||||
"ledsCanDim" : false,
|
||||
"vendorFOV" : 75.76079874010732
|
||||
|
||||
68
docs/source/docs/contributing/guidelines.md
Normal file
68
docs/source/docs/contributing/guidelines.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Welcome!
|
||||
|
||||
First and foremost, welcome to PhotonVision Development! We're pumped that you're interested to jump in, and help out!
|
||||
|
||||
Like most things in FIRST, PhotonVision is reliant on the hard work of dedicated volunteers. It doesn't exist without you. You're joining in with a strong tradition of open-source software development.
|
||||
|
||||
This page talks a bit about how we develop PhotonVision, as a community. It applies to all repos and community aspects for PhotonVision.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The very first thing we'd recommend - get your computer set up to be able to build and run PhotonVision. [Docs for that are here](building-photon.md).
|
||||
|
||||
The two best ways to figure out what to do first:
|
||||
|
||||
1. Take a look at [the main PhotonVision Repo's Issues](https://github.com/PhotonVision/photonvision/issues) - especially those marked `bug` or `good first issue`!
|
||||
2. Connect on [the Discord Server](https://discord.gg/wYxTwym) - Introduce yourself, and talk about what you're interested in!
|
||||
|
||||
From there - assign yourself to an issue if you intend to work it. Create a Fork in github, and make and test your changes. Once passing builds, meeting your expectations, and passing CI, open a pull request back to the main repo.
|
||||
|
||||
## Submitting A PR
|
||||
|
||||
Pull Requests are the mechanism used to ensure we only merge high-quality, reviewed, concrete, and organized changes to the codebase.
|
||||
|
||||
Things that peer reviewers will look for include:
|
||||
|
||||
* All CI checks are passing on the server.
|
||||
* Documentation - does the PR match the issue? Is the description detailed?
|
||||
* Cohesiveness - does the PR express a singular, related set of changes?
|
||||
* Architecture - is the code consistent with other code around it? Is it maintainable for the long term?
|
||||
* Testing - have unit tests been added as appropriate? Has the change been tested on real hardware?
|
||||
|
||||
Work as you can to clean up any changes requested. Once all changes are addressed, the contain should get merged, and included in the next release! Horary!
|
||||
|
||||
## General Developer Interaction
|
||||
|
||||
The main guiding principle: remember that we're all volunteers. While promptness is always appreciated (and occasionally required as release deadlines approach), it's important to remember each individual is only contributing when their personal schedule allows. Expect delays, prefer asynchronous communication, and be polite with reminders.
|
||||
|
||||
While most of the community members are either FIRST robotics mentors or students, the PhotonVision development team is primarily focused on delivering high quality software. Mentorship can and does occur, but is not the primary goal. Members are expected to do their learning fairly independently.
|
||||
|
||||
Seek to build trust in the quality of your work. Think carefully on your opinions before asking others to think about them too.
|
||||
|
||||
Bias toward action, and productionizable code. Limit the number of active PR's to help keep focus.
|
||||
|
||||
Finally, be sure to embody the ethos of Gracious Professionalism in all your actions, on all platforms in the project. See more in our [code of conduct](https://github.com/PhotonVision/.github/blob/738dfcb792fdbfc2e8408c0135e389179fc483c0/codeofcoduct.md)
|
||||
|
||||
### AI Usage
|
||||
|
||||
Coding assistants driven by Large Language Models ("AI") are extremely powerful tools, and have been used on more than one occasion to accelerate development.
|
||||
|
||||
PhotonVision still maintains a fundamental philosophy that the human submitting the pull request is responsible for the code and its behavior, regardless of the tools used to create it.
|
||||
|
||||
These tools can also generate a large volume of code changes, very rapidly. The above guidelines on PR quality still apply - large, undirected, or overly-scoped PR's are likely to be ignored, regardless of tooling used to generate them.
|
||||
|
||||
### Violations
|
||||
|
||||
We're thankful that we've rarely experienced major issues in our community. In all cases, the project leads shall have the final decision making authority when dealing with violations of these guidelines.
|
||||
|
||||
## Yearly Development Cycle
|
||||
|
||||
PhotonVision's Development Cycle follows the same general flow as the FRC Build Season. Larger experiments get done over the summer, fall focuses on testing and "production-readiness", build season focuses on keeping all teams running smoothly.
|
||||
|
||||
The actual priorities also shift as developers have more or time to commit, or express interest in specific direction.
|
||||
|
||||
PR's are absolutely always welcome. Just note depending on the scope and size, they may not be reviewed or merged immediately.
|
||||
|
||||
## Project Governance
|
||||
|
||||
This project is jointly lead by Matt and Banks, who may be contacted on Discord. They serve as a "Benevolent leader for life" role, coordinating and approving architecture as needed, and curating the team of developers who have Pull Request review and merge responsibilities.
|
||||
@@ -1,6 +1,7 @@
|
||||
# Contributing to PhotonVision Projects
|
||||
|
||||
```{toctree}
|
||||
guidelines
|
||||
building-photon
|
||||
building-docs
|
||||
linting
|
||||
|
||||
@@ -103,9 +103,9 @@ If your hardware contains a camera with a known field of vision, it can be enter
|
||||
}
|
||||
```
|
||||
|
||||
## Cosmetic & Branding
|
||||
## Device Name Branding
|
||||
|
||||
To help differentiate your hardware from other solutions, some customization is allowed.
|
||||
To help differentiate your hardware from other solutions, a device name may be set.
|
||||
|
||||
```{eval-rst}
|
||||
.. tab-set-code::
|
||||
@@ -113,8 +113,6 @@ To help differentiate your hardware from other solutions, some customization is
|
||||
|
||||
{
|
||||
"deviceName" : "Super Cool Custom Hardware",
|
||||
"deviceLogoPath" : "",
|
||||
"supportURL" : "https://cat-bounce.com/",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -132,8 +130,6 @@ Here is a complete example `hardwareConfig.json`:
|
||||
|
||||
{
|
||||
"deviceName" : "Blinky McBlinkface",
|
||||
"deviceLogoPath" : "",
|
||||
"supportURL" : "https://www.youtube.com/watch?v=b-CvLWbfZhU",
|
||||
"ledPins" : [2, 13],
|
||||
"ledsCanDim" : true,
|
||||
"ledBrightnessRange" : [ 0, 100 ],
|
||||
|
||||
@@ -52,10 +52,10 @@ Only use a static IP when connected to the **robot radio**, and never when testi
|
||||
3. Open the settings tab on the left pane.
|
||||
4. Under the Networking section, set your team number.
|
||||
5. Change your IP to Static.
|
||||
6. Set your coprocessor's IP address to “10.TE.AM.11”. More information on IP format can be found [here](https://docs.wpilib.org/en/stable/docs/networking/networking-introduction/ip-configurations.html#on-the-field-static-configuration).
|
||||
6. Set your coprocessor's IP address to “10.TE.AM.xx”. "xx" should be a unique number not currently used by another device on the robot in the `.6-.19` range. More information on IP format can be found [here](https://docs.wpilib.org/en/stable/docs/networking/networking-introduction/ip-configurations.html#on-the-field-static-configuration).
|
||||
7. Click the “Save” button.
|
||||
|
||||
Power-cycle your robot and then you will now be access the PhotonVision dashboard at `10.TE.AM.11:5800`.
|
||||
Power-cycle your robot and then you will now be access the PhotonVision dashboard at `10.TE.AM.xx:5800`.
|
||||
|
||||
```{image} images/static.png
|
||||
:alt: Correctly set static IP
|
||||
|
||||
@@ -95,6 +95,27 @@ These `TargetModel` are paired with a target pose to create a `VisionTargetSim`.
|
||||
The pose of a `VisionTargetSim` object can be updated to simulate moving targets. Note, however, that this will break latency simulation for that target.
|
||||
:::
|
||||
|
||||
To use simulated object detection, you must provide an objDetClassId (zero-indexed class ID) and confidence value. When you set objDetConf to -1, the simulation computes confidence based on the area of the target in the camera's field of view. To simulate a object detection model with one class (fuel, index 0) and specify confidence, you'd write:
|
||||
|
||||
```{eval-rst}
|
||||
.. tab-set-code::
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
// arbitrary position on field
|
||||
final var targetPose = new Pose3d(new Translation3d(2, 0, 0), new Rotation3d());
|
||||
// Class id, zero-indexed
|
||||
final int classId = 0;
|
||||
// Confidence, between 0 and 1.
|
||||
final float conf = 0.67f;
|
||||
// 6 inch diameter ball
|
||||
final TargetModel ballModel = new TargetModel(Units.inchesToMeters(6));
|
||||
final var ballTargetSim = new VisionTargetSim(targetPose, ballModel, classId, conf);
|
||||
|
||||
// Add this vision target to the vision system simulation to make it visible
|
||||
visionSim.addVisionTargets(visionTarget);
|
||||
```
|
||||
|
||||
For convenience, an `AprilTagFieldLayout` can also be added to automatically create a target for each of its AprilTags.
|
||||
|
||||
```{eval-rst}
|
||||
@@ -196,6 +217,42 @@ If the camera is mounted on a mobile mechanism (like a turret) this transform ca
|
||||
visionSim.adjustCamera(cameraSim, robotToCamera);
|
||||
```
|
||||
|
||||
## Low-Resource Vision Simulation with Photonvision
|
||||
|
||||
By default, PhotonCameraSim renders two simulated camera streams using OpenCV:
|
||||
|
||||
- Raw stream - The unprocessed camera view
|
||||
- Processed stream - The camera view with vision processing overlays
|
||||
|
||||
These streams are nice if you want to actually view the simulated images, but they can be computationally expensive. This may cause lag and reduced simulation performance on lower-powered computers.
|
||||
Lightweight Configuration
|
||||
|
||||
The following configuration disables both streams while still allowing tag detection and pose simulation to work. It's not perfect, but it's much better performance-wise than the default configuration.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
// lightweight config version
|
||||
// var cameraProperties = new SimCameraProperties();
|
||||
// cameraSim = new PhotonCameraSim(camera, cameraProperties, aprilTagLayout);
|
||||
// cameraSim.enableRawStream(false); // disables raw image stream
|
||||
// cameraSim.enableProcessedStream(false); // disables processed image stream
|
||||
|
||||
**Use Case**
|
||||
|
||||
This configuration is ideal for Chromebooks or low-spec machines where rendering the simulated camera images causes lag, but vision data is still desired for testing.
|
||||
|
||||
**What Still Works**
|
||||
|
||||
- AprilTag detection
|
||||
- Pose estimation
|
||||
- NetworkTables data publishing
|
||||
- Robot positioning and targeting
|
||||
|
||||
**What's Disabled**
|
||||
|
||||
- Visual camera stream rendering
|
||||
- Real-time visual debugging of camera output
|
||||
|
||||
## Updating The Simulation World
|
||||
|
||||
To update the `VisionSystemSim`, we simply have to pass in the simulated robot pose periodically (in `simulationPeriodic()`).
|
||||
|
||||
@@ -4,7 +4,7 @@ import { defineConfigWithVueTs, vueTsConfigs } from "@vue/eslint-config-typescri
|
||||
import skipFormattingConfig from "@vue/eslint-config-prettier/skip-formatting";
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
pluginVue.configs["flat/recommended"],
|
||||
pluginVue.configs["flat/recommended-error"],
|
||||
vueTsConfigs.recommended,
|
||||
skipFormattingConfig,
|
||||
{
|
||||
@@ -26,10 +26,24 @@ export default defineConfigWithVueTs(
|
||||
|
||||
semi: ["error", "always"],
|
||||
"eol-last": "error",
|
||||
eqeqeq: "error",
|
||||
"no-useless-concat": "error",
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"quote-props": ["error", "as-needed"],
|
||||
"no-case-declarations": "off",
|
||||
"vue/eqeqeq": "error",
|
||||
"vue/no-useless-concat": "error",
|
||||
"vue/no-constant-condition": "error",
|
||||
"vue/no-empty-pattern": "error",
|
||||
"vue/no-undef-directives": "error",
|
||||
"vue/no-undef-properties": "error",
|
||||
"vue/no-unused-properties": "error",
|
||||
"vue/no-unused-refs": "error",
|
||||
"vue/no-use-v-else-with-v-for": "error",
|
||||
"vue/no-useless-mustaches": "error",
|
||||
"vue/no-useless-v-bind": "error",
|
||||
"vue/require-default-prop": "off",
|
||||
"vue/v-for-delimiter-style": "error",
|
||||
"vue/v-on-event-hyphenation": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"vue/valid-v-slot": ["error", { allowModifiers: true }]
|
||||
|
||||
@@ -41,11 +41,11 @@
|
||||
"@vue/eslint-config-typescript": "^14.5.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-plugin-vue": "^10.3.0",
|
||||
"eslint-plugin-vue": "^10.7.0",
|
||||
"prettier": "^3.6.2",
|
||||
"sass": "^1.89.2",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.0.5",
|
||||
"vite": "^8.0.2",
|
||||
"vite-plugin-vuetify": "^2.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export default defineConfig({
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
command: process.platform == "win32" ? "" : "./" + "gradlew run",
|
||||
command: process.platform === "win32" ? "" : "./gradlew run",
|
||||
url: "http://localhost:5800",
|
||||
timeout: 300 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
|
||||
795
photon-client/pnpm-lock.yaml
generated
795
photon-client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -50,8 +50,8 @@ const drawTargets = async (targets: PhotonTarget[]) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (theme.global.name.value === "LightTheme") scene.background = new Color(0xa9a9a9);
|
||||
else scene.background = new Color(0x000000);
|
||||
if (theme.global.current.value.dark) scene.background = new Color(0x000000);
|
||||
else scene.background = new Color(0xa9a9a9);
|
||||
|
||||
scene.remove(...previousTargets);
|
||||
previousTargets = [];
|
||||
@@ -158,8 +158,8 @@ onMounted(async () => {
|
||||
if (!canvas) return;
|
||||
renderer = new WebGLRenderer({ canvas: canvas });
|
||||
|
||||
if (theme.global.name.value === "LightTheme") scene.background = new Color(0xa9a9a9);
|
||||
else scene.background = new Color(0x000000);
|
||||
if (theme.global.current.value.dark) scene.background = new Color(0x000000);
|
||||
else scene.background = new Color(0xa9a9a9);
|
||||
|
||||
onWindowResize();
|
||||
window.addEventListener("resize", onWindowResize);
|
||||
@@ -234,7 +234,7 @@ watchEffect(() => {
|
||||
<v-btn
|
||||
style="width: 100%"
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="resetCamFirstPerson"
|
||||
>
|
||||
First Person
|
||||
@@ -244,7 +244,7 @@ watchEffect(() => {
|
||||
<v-btn
|
||||
style="width: 100%"
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="resetCamThirdPerson"
|
||||
>
|
||||
Third Person
|
||||
|
||||
@@ -208,8 +208,8 @@ onMounted(async () => {
|
||||
const ambientLight = new AmbientLight(0xffffff, 0.6);
|
||||
scene.add(ambientLight);
|
||||
|
||||
if (theme.global.name.value === "LightTheme") scene.background = new Color(0xa9a9a9);
|
||||
else scene.background = new Color(0x000000);
|
||||
if (theme.global.current.value.dark) scene.background = new Color(0x000000);
|
||||
else scene.background = new Color(0xa9a9a9);
|
||||
|
||||
// Initialize a stable aspect ratio so subsequent resize events derive
|
||||
// height from width, avoiding layout feedback during continuous resizing
|
||||
@@ -347,7 +347,7 @@ watch(
|
||||
<v-btn
|
||||
style="width: 100%"
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="resetCamFirstPerson"
|
||||
>
|
||||
First Person
|
||||
@@ -357,7 +357,7 @@ watch(
|
||||
<v-btn
|
||||
style="width: 100%"
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="resetCamThirdPerson"
|
||||
>
|
||||
Third Person
|
||||
|
||||
@@ -50,7 +50,7 @@ const containerStyle = computed<StyleValue>(() => {
|
||||
});
|
||||
|
||||
const overlayStyle = computed<StyleValue>(() => {
|
||||
if (useStateStore().colorPickingMode || streamSrc.value == emptyStreamSrc) {
|
||||
if (useStateStore().colorPickingMode || streamSrc.value === emptyStreamSrc) {
|
||||
return { display: "none" };
|
||||
} else {
|
||||
return {};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useStateStore } from "@/stores/StateStore";
|
||||
{{ useStateStore().snackbarData.message }}
|
||||
</p>
|
||||
<v-progress-linear
|
||||
v-if="useStateStore().snackbarData.progressBar != -1"
|
||||
v-if="useStateStore().snackbarData.progressBar !== -1"
|
||||
v-model="useStateStore().snackbarData.progressBar"
|
||||
height="15"
|
||||
:color="useStateStore().snackbarData.progressBarColor"
|
||||
|
||||
@@ -38,7 +38,7 @@ const renderCompact = computed<boolean>(() => compact.value || !mdAndUp.value);
|
||||
<v-list-item link to="/settings" prepend-icon="mdi-cog">
|
||||
<v-list-item-title>Settings</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item ref="camerasTabOpener" link to="/cameras" prepend-icon="mdi-camera">
|
||||
<v-list-item link to="/cameras" prepend-icon="mdi-camera">
|
||||
<v-list-item-title>Camera</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
@@ -73,7 +73,7 @@ const renderCompact = computed<boolean>(() => compact.value || !mdAndUp.value);
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
link
|
||||
:prepend-icon="theme.global.name.value === 'LightTheme' ? 'mdi-white-balance-sunny' : 'mdi-weather-night'"
|
||||
:prepend-icon="theme.global.current.value.dark ? 'mdi-weather-night' : 'mdi-white-balance-sunny'"
|
||||
@click="() => toggleTheme(theme)"
|
||||
>
|
||||
<v-list-item-title>Theme</v-list-item-title>
|
||||
|
||||
@@ -28,7 +28,7 @@ const getUniqueVideoFormatsByResolution = (): VideoFormat[] => {
|
||||
if (useCameraSettingsStore().currentCameraSettings.validVideoFormats.length === 0) return uniqueResolutions;
|
||||
useCameraSettingsStore().currentCameraSettings.validVideoFormats.forEach((format) => {
|
||||
const index = uniqueResolutions.findIndex((v) => resolutionsAreEqual(v.resolution, format.resolution));
|
||||
const contains = index != -1;
|
||||
const contains = index !== -1;
|
||||
let skip = false;
|
||||
if (contains && format.fps > uniqueResolutions[index].fps) {
|
||||
uniqueResolutions.splice(index, 1);
|
||||
@@ -132,7 +132,7 @@ const downloadCalibBoard = async () => {
|
||||
const yPos = chessboardStartY + squareY * squareSizeIn.value;
|
||||
|
||||
// Only draw the odd squares to create the chessboard pattern
|
||||
if (squareY % 2 != squareX % 2) {
|
||||
if (squareY % 2 !== squareX % 2) {
|
||||
doc.rect(xPos, yPos, squareSizeIn.value, squareSizeIn.value, "F");
|
||||
}
|
||||
}
|
||||
@@ -293,7 +293,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
<v-card-subtitle v-if="!isCalibrating" class="pl-0 pb-3 pt-4 opacity-100"
|
||||
>Configure New Calibration</v-card-subtitle
|
||||
>
|
||||
<v-form ref="form" v-model="settingsValid">
|
||||
<v-form v-model="settingsValid">
|
||||
<pv-select
|
||||
v-model="uniqueVideoResolutionString"
|
||||
label="Resolution"
|
||||
@@ -470,7 +470,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
closable
|
||||
density="compact"
|
||||
class="mb-5"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
:color="useSettingsStore().general.mrCalWorking ? 'buttonPassive' : 'error'"
|
||||
:icon="useSettingsStore().general.mrCalWorking ? 'mdi-check' : 'mdi-close'"
|
||||
:text="
|
||||
@@ -481,7 +481,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
/>
|
||||
<div v-if="isCalibrating" class="d-flex justify-center align-center pb-5">
|
||||
<v-chip
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
label
|
||||
:color="useStateStore().calibrationData.hasEnoughImages ? 'buttonPassive' : 'light-grey'"
|
||||
>
|
||||
@@ -494,7 +494,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
color="buttonPassive"
|
||||
size="small"
|
||||
block
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:disabled="!settingsValid"
|
||||
@click="downloadCalibBoard"
|
||||
>
|
||||
@@ -509,7 +509,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
density="compact"
|
||||
text="Too many corners. Finish calibration now!"
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
<div class="d-flex pt-5">
|
||||
<v-col cols="6" class="pa-0 pr-2">
|
||||
@@ -517,7 +517,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
size="small"
|
||||
block
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:disabled="!settingsValid || tooManyPoints"
|
||||
@click="isCalibrating ? useCameraSettingsStore().takeCalibrationSnapshot() : startCalibration()"
|
||||
>
|
||||
@@ -531,7 +531,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
|
||||
<v-btn
|
||||
size="small"
|
||||
block
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:color="useStateStore().calibrationData.hasEnoughImages ? 'buttonActive' : 'error'"
|
||||
:disabled="!isCalibrating || !settingsValid"
|
||||
@click="endCalibration"
|
||||
|
||||
@@ -40,8 +40,8 @@ const importCalibration = async () => {
|
||||
const data = await parseJsonFile<CameraCalibrationResult>(uploadedJson);
|
||||
|
||||
if (
|
||||
data.resolution.height != props.videoFormat.resolution.height ||
|
||||
data.resolution.width != props.videoFormat.resolution.width
|
||||
data.resolution.height !== props.videoFormat.resolution.height ||
|
||||
data.resolution.width !== props.videoFormat.resolution.width
|
||||
) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
@@ -121,7 +121,7 @@ const viewingImg = ref(0);
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openUploadPhotonCalibJsonPrompt"
|
||||
>
|
||||
<v-icon start size="large"> mdi-import</v-icon>
|
||||
@@ -140,7 +140,7 @@ const viewingImg = ref(0);
|
||||
color="buttonPassive"
|
||||
:disabled="!currentCalibrationCoeffs"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openExportCalibrationPrompt"
|
||||
>
|
||||
<v-icon start size="large">mdi-export</v-icon>
|
||||
@@ -318,7 +318,7 @@ const viewingImg = ref(0);
|
||||
color="primary"
|
||||
text="The selected video format has not been calibrated."
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
</div>
|
||||
<Suspense v-else-if="tab === 'details'">
|
||||
@@ -341,7 +341,7 @@ const viewingImg = ref(0);
|
||||
<pv-delete-modal
|
||||
v-model="confirmRemoveDialog.show"
|
||||
:width="500"
|
||||
:title="'Delete Calibration'"
|
||||
title="Delete Calibration"
|
||||
:description="`Are you sure you want to delete the calibration for '${confirmRemoveDialog.vf.resolution.width}x${confirmRemoveDialog.vf.resolution.height}'? This action cannot be undone.`"
|
||||
:on-confirm="() => removeCalibration(confirmRemoveDialog.vf)"
|
||||
/>
|
||||
|
||||
@@ -99,7 +99,7 @@ const expanded = ref([]);
|
||||
<v-card-text class="pt-0">
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'tonal'"
|
||||
@click="fetchSnapshots"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-folder </v-icon>
|
||||
@@ -115,7 +115,7 @@ const expanded = ref([]);
|
||||
density="compact"
|
||||
text="There are currently no saved snapshots."
|
||||
icon="mdi-information-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-text v-else class="pt-0">
|
||||
@@ -125,7 +125,7 @@ const expanded = ref([]);
|
||||
density="compact"
|
||||
text="Snapshot timestamps depend on when the coprocessor was last connected to the internet."
|
||||
icon="mdi-information-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
<v-data-table
|
||||
v-model:expanded="expanded"
|
||||
|
||||
@@ -66,10 +66,10 @@ const settingsHaveChanged = (): boolean => {
|
||||
const b = useCameraSettingsStore().currentCameraSettings;
|
||||
|
||||
for (const q in ValidQuirks) {
|
||||
if (a.quirksToChange[q] != b.cameraQuirks.quirks[q]) return true;
|
||||
if (a.quirksToChange[q] !== b.cameraQuirks.quirks[q]) return true;
|
||||
}
|
||||
|
||||
return a.fov != b.fov.value;
|
||||
return a.fov !== b.fov.value;
|
||||
};
|
||||
|
||||
const resetTempSettingsStruct = () => {
|
||||
@@ -179,7 +179,7 @@ const wrappedCameras = computed<SelectItem[]>(() =>
|
||||
size="small"
|
||||
color="buttonActive"
|
||||
:disabled="!settingsHaveChanged()"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="saveCameraSettings"
|
||||
>
|
||||
<v-icon start size="large"> mdi-content-save </v-icon>
|
||||
@@ -191,7 +191,7 @@ const wrappedCameras = computed<SelectItem[]>(() =>
|
||||
block
|
||||
size="small"
|
||||
color="error"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showDeleteCamera = true)"
|
||||
>
|
||||
<v-icon start size="large"> mdi-trash-can-outline </v-icon>
|
||||
|
||||
@@ -103,7 +103,7 @@ const fpsTooLow = computed<boolean>(() => {
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
class="fill"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:disabled="
|
||||
useCameraSettingsStore().isDriverMode ||
|
||||
useCameraSettingsStore().isCalibrationMode ||
|
||||
@@ -116,7 +116,7 @@ const fpsTooLow = computed<boolean>(() => {
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
class="fill"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:disabled="
|
||||
useCameraSettingsStore().isDriverMode ||
|
||||
useCameraSettingsStore().isCalibrationMode ||
|
||||
|
||||
@@ -49,7 +49,7 @@ const confirmationText = ref("");
|
||||
color="buttonActive"
|
||||
style="float: right"
|
||||
width="100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="onBackup"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-export </v-icon>
|
||||
@@ -65,7 +65,7 @@ const confirmationText = ref("");
|
||||
? confirmationText.toLowerCase() !== expectedConfirmationText.toLowerCase()
|
||||
: false
|
||||
"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="
|
||||
onConfirm();
|
||||
confirmationText = '';
|
||||
|
||||
@@ -407,14 +407,14 @@ const wrappedCameras = computed<SelectItem[]>(() =>
|
||||
<v-card-actions class="pr-5 pt-10px pb-5">
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="cancelPipelineCreation"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:disabled="checkPipelineName(newPipelineName) !== true"
|
||||
@click="createNewPipeline"
|
||||
>
|
||||
@@ -441,7 +441,7 @@ const wrappedCameras = computed<SelectItem[]>(() =>
|
||||
<v-card-actions class="pa-5 pt-0">
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
class="text-black"
|
||||
@click="cancelChangePipelineType"
|
||||
>
|
||||
@@ -449,7 +449,7 @@ const wrappedCameras = computed<SelectItem[]>(() =>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="confirmChangePipelineType"
|
||||
>
|
||||
Confirm
|
||||
|
||||
@@ -122,14 +122,14 @@ const onBeforeTabUpdate = () => {
|
||||
density="compact"
|
||||
text="Camera is not connected. Please check your connection and try again."
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-col
|
||||
v-for="(tabGroupData, tabGroupIndex) in tabGroups"
|
||||
:key="tabGroupIndex"
|
||||
:cols="tabGroupIndex == 1 && useCameraSettingsStore().currentPipelineSettings.doMultiTarget ? 7 : ''"
|
||||
:cols="tabGroupIndex === 1 && useCameraSettingsStore().currentPipelineSettings.doMultiTarget ? 7 : ''"
|
||||
:class="tabGroupIndex !== tabGroups.length - 1 && 'pr-3'"
|
||||
@vue:before-update="onBeforeTabUpdate"
|
||||
>
|
||||
|
||||
@@ -35,7 +35,7 @@ const processingMode = computed<number>({
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:disabled="!useCameraSettingsStore().hasConnected"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
class="w-50"
|
||||
>
|
||||
<template #prepend>
|
||||
@@ -48,10 +48,10 @@ const processingMode = computed<number>({
|
||||
:disabled="
|
||||
!useCameraSettingsStore().hasConnected ||
|
||||
!useCameraSettingsStore().isCurrentVideoFormatCalibrated ||
|
||||
useCameraSettingsStore().currentPipelineSettings.pipelineType == PipelineType.ObjectDetection ||
|
||||
useCameraSettingsStore().currentPipelineSettings.pipelineType == PipelineType.ColoredShape
|
||||
useCameraSettingsStore().currentPipelineSettings.pipelineType === PipelineType.ObjectDetection ||
|
||||
useCameraSettingsStore().currentPipelineSettings.pipelineType === PipelineType.ColoredShape
|
||||
"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
class="w-50"
|
||||
>
|
||||
<template #prepend>
|
||||
@@ -69,7 +69,7 @@ const processingMode = computed<number>({
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
class="fill w-50"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
>
|
||||
<v-icon start class="mode-btn-icon" size="large">mdi-import</v-icon>
|
||||
<span class="mode-btn-label">Raw</span>
|
||||
@@ -77,7 +77,7 @@ const processingMode = computed<number>({
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
class="fill w-50"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
>
|
||||
<v-icon start class="mode-btn-icon" size="large">mdi-export</v-icon>
|
||||
<span class="mode-btn-label">Processed</span>
|
||||
|
||||
@@ -168,7 +168,7 @@ const interactiveCols = computed(() =>
|
||||
block
|
||||
color="primary"
|
||||
class="text-black"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="useCameraSettingsStore().takeRobotOffsetPoint(RobotOffsetType.Single)"
|
||||
>
|
||||
Take Point
|
||||
@@ -179,7 +179,7 @@ const interactiveCols = computed(() =>
|
||||
size="small"
|
||||
block
|
||||
color="error"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="useCameraSettingsStore().takeRobotOffsetPoint(RobotOffsetType.Clear)"
|
||||
>
|
||||
Clear All Points
|
||||
@@ -196,7 +196,7 @@ const interactiveCols = computed(() =>
|
||||
block
|
||||
color="primary"
|
||||
class="text-black"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="useCameraSettingsStore().takeRobotOffsetPoint(RobotOffsetType.DualFirst)"
|
||||
>
|
||||
Take First Point
|
||||
@@ -208,7 +208,7 @@ const interactiveCols = computed(() =>
|
||||
block
|
||||
color="primary"
|
||||
class="text-black"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="useCameraSettingsStore().takeRobotOffsetPoint(RobotOffsetType.DualSecond)"
|
||||
>
|
||||
Take Second Point
|
||||
@@ -219,7 +219,7 @@ const interactiveCols = computed(() =>
|
||||
size="small"
|
||||
block
|
||||
color="error"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="useCameraSettingsStore().takeRobotOffsetPoint(RobotOffsetType.Clear)"
|
||||
>
|
||||
Clear All Points
|
||||
|
||||
@@ -207,7 +207,7 @@ const resetCurrentBuffer = () => {
|
||||
color="buttonActive"
|
||||
class="mb-4 mt-1"
|
||||
style="width: min-content"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="resetCurrentBuffer"
|
||||
>Reset Samples</v-btn
|
||||
>
|
||||
|
||||
@@ -191,7 +191,7 @@ const interactiveCols = computed(() =>
|
||||
block
|
||||
color="primary"
|
||||
class="text-black"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="enableColorPicking(useCameraSettingsStore().currentPipelineSettings.hueInverted ? 2 : 3)"
|
||||
>
|
||||
<v-icon start size="large"> mdi-minus </v-icon>
|
||||
@@ -204,7 +204,7 @@ const interactiveCols = computed(() =>
|
||||
class="text-black"
|
||||
size="small"
|
||||
block
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="enableColorPicking(1)"
|
||||
>
|
||||
<v-icon start size="large"> mdi-plus-minus </v-icon>
|
||||
@@ -217,7 +217,7 @@ const interactiveCols = computed(() =>
|
||||
block
|
||||
color="primary"
|
||||
class="text-black"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="enableColorPicking(useCameraSettingsStore().currentPipelineSettings.hueInverted ? 3 : 2)"
|
||||
>
|
||||
<v-icon start size="large"> mdi-plus </v-icon>
|
||||
@@ -232,7 +232,7 @@ const interactiveCols = computed(() =>
|
||||
color="primary"
|
||||
class="text-black"
|
||||
size="small"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="disableColorPicking"
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -312,7 +312,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="useStateStore().showLogModal = true"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-eye </v-icon>
|
||||
@@ -322,7 +322,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openExportLogsPrompt"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-download </v-icon>
|
||||
@@ -345,7 +345,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showImportDialog = true)"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-import </v-icon>
|
||||
@@ -355,7 +355,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openExportSettingsPrompt"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-export </v-icon>
|
||||
@@ -369,7 +369,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col cols="12" sm="6"
|
||||
><v-btn
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="restartProgram"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-restart </v-icon>
|
||||
@@ -379,7 +379,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col cols="12" sm="6">
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openOfflineUpdatePrompt"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-upload </v-icon>
|
||||
@@ -400,7 +400,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col cols="12" sm="6">
|
||||
<v-btn
|
||||
color="buttonActive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="restartDevice"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-restart-alert </v-icon>
|
||||
@@ -410,7 +410,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-col cols="12" sm="6">
|
||||
<v-btn
|
||||
color="error"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showFactoryReset = true)"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-trash-can-outline </v-icon>
|
||||
@@ -453,7 +453,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-card-text class="pt-0 flex-0-0 pb-2">
|
||||
<div class="d-flex justify-space-between pb-3 pt-3">
|
||||
<span>CPU Temperature</span>
|
||||
<span>{{ cpuTempData.at(-1)?.value == -1 ? "--- " : Math.round(cpuTempData.at(-1)?.value ?? 0) }}°C</span>
|
||||
<span>{{ cpuTempData.at(-1)?.value === -1 ? "--- " : Math.round(cpuTempData.at(-1)?.value ?? 0) }}°C</span>
|
||||
</div>
|
||||
<Suspense>
|
||||
<!-- Allows us to import echarts when it's actually needed -->
|
||||
@@ -470,7 +470,10 @@ watch(metricsHistorySnapshot, () => {
|
||||
tooltip="Measured rate for this coprocessor ONLY. This FMS limit is for ALL robot communication. If you are experiencing bandwidth issues while under this limit, check other sources."
|
||||
/>
|
||||
<span
|
||||
>{{ networkUsageData.at(-1)?.value == -1 ? "---" : networkUsageData.at(-1)?.value.toFixed(3) }} Mb/s</span
|
||||
>{{
|
||||
networkUsageData.at(-1)?.value === -1 ? "---" : networkUsageData.at(-1)?.value.toFixed(3)
|
||||
}}
|
||||
Mb/s</span
|
||||
>
|
||||
</div>
|
||||
<Suspense>
|
||||
@@ -529,7 +532,7 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-btn
|
||||
color="primary"
|
||||
:disabled="importFile === null"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="handleSettingsImport"
|
||||
>
|
||||
<v-icon start class="open-icon"> mdi-import </v-icon>
|
||||
@@ -552,10 +555,10 @@ watch(metricsHistorySnapshot, () => {
|
||||
<v-btn
|
||||
color="buttonActive"
|
||||
width="100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="
|
||||
offlineUpdateDialog.show = false;
|
||||
handleOfflineUpdate(offlineUpdate.value.files[0]);
|
||||
handleOfflineUpdate(offlineUpdate.files[0]);
|
||||
"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-upload </v-icon>
|
||||
|
||||
@@ -185,7 +185,7 @@ watchEffect(() => {
|
||||
</v-card-title>
|
||||
<div class="pa-5 pt-0">
|
||||
<v-card-title class="pl-0 pt-0 pb-10px">Networking</v-card-title>
|
||||
<v-form ref="form" v-model="settingsValid">
|
||||
<v-form v-model="settingsValid">
|
||||
<pv-input
|
||||
v-model="tempSettingsStruct.ntServerAddress"
|
||||
label="Team Number/NetworkTables Server Address"
|
||||
@@ -205,7 +205,7 @@ watchEffect(() => {
|
||||
density="compact"
|
||||
text="The NetworkTables Server Address is not set or is invalid. NetworkTables is unable to connect."
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
<pv-radio
|
||||
v-show="!useSettingsStore().network.networkingDisabled"
|
||||
@@ -279,7 +279,7 @@ watchEffect(() => {
|
||||
density="compact"
|
||||
text="Cannot detect any wired connections! Send program logs to the developers for help."
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
<pv-switch
|
||||
v-model="tempSettingsStruct.runNTServer"
|
||||
@@ -293,7 +293,7 @@ watchEffect(() => {
|
||||
density="compact"
|
||||
text="This mode is intended for debugging and should be off for proper usage. PhotonLib will NOT work!"
|
||||
icon="mdi-information-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
<v-card-title class="pl-0 pt-3 pb-10px">Miscellaneous</v-card-title>
|
||||
<pv-switch
|
||||
@@ -308,13 +308,13 @@ watchEffect(() => {
|
||||
density="compact"
|
||||
text="This mode is intended for debugging and may reduce performance; it should be off for field use."
|
||||
icon="mdi-information-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
</v-form>
|
||||
<v-btn
|
||||
color="primary"
|
||||
class="mt-3"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
style="color: black; width: 100%"
|
||||
:disabled="!settingsValid || !settingsHaveChanged()"
|
||||
@click="saveGeneralSettings"
|
||||
@@ -377,7 +377,7 @@ watchEffect(() => {
|
||||
</v-card-text>
|
||||
<v-card-actions class="pa-5 pt-0">
|
||||
<v-btn
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
color="buttonPassive"
|
||||
class="text-black"
|
||||
@click="showThemeConfig = false"
|
||||
@@ -385,7 +385,7 @@ watchEffect(() => {
|
||||
Close
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
color="buttonActive"
|
||||
class="text-black"
|
||||
@click="
|
||||
|
||||
@@ -53,9 +53,9 @@ const getOptions = (data: ChartData[] = []) => {
|
||||
|
||||
return `${tooltip}</div>`;
|
||||
},
|
||||
backgroundColor: theme.themes.value[theme.global.name.value].colors.background,
|
||||
backgroundColor: theme.global.current.value.colors.background,
|
||||
textStyle: {
|
||||
color: theme.themes.value[theme.global.name.value].colors.onBackground
|
||||
color: theme.global.current.value.colors.onBackground
|
||||
},
|
||||
axisPointer: {
|
||||
animation: false
|
||||
@@ -149,10 +149,9 @@ const getSeries = (data: ChartData[] = []) => {
|
||||
: null,
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
color:
|
||||
theme.global.name.value === "LightTheme"
|
||||
? theme.themes.value[theme.global.name.value].colors.primary
|
||||
: `rgb(${color.r}, ${color.g}, ${color.b})`
|
||||
color: theme.global.current.value.dark
|
||||
? `rgb(${color.r}, ${color.g}, ${color.b})`
|
||||
: theme.global.current.value.colors.primary
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
@@ -164,17 +163,15 @@ const getSeries = (data: ChartData[] = []) => {
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color:
|
||||
theme.global.name.value === "LightTheme"
|
||||
? `${theme.themes.value[theme.global.name.value].colors.primary}40`
|
||||
: `rgba(${color.r}, ${color.g}, ${color.b}, 0.15)`
|
||||
color: theme.global.current.value.dark
|
||||
? `rgba(${color.r}, ${color.g}, ${color.b}, 0.15)`
|
||||
: `${theme.global.current.value.colors.primary}40`
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color:
|
||||
theme.global.name.value === "LightTheme"
|
||||
? `${theme.themes.value[theme.global.name.value].colors.primary}40`
|
||||
: `rgba(${color.r}, ${color.g}, ${color.b}, 0.15)`
|
||||
color: theme.global.current.value.dark
|
||||
? `rgba(${color.r}, ${color.g}, ${color.b}, 0.15)`
|
||||
: `${theme.global.current.value.colors.primary}40`
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ const handleBulkImport = async () => {
|
||||
<v-btn
|
||||
color="buttonActive"
|
||||
class="justify-center"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showImportDialog = true)"
|
||||
>
|
||||
<v-icon start class="open-icon"> mdi-import </v-icon>
|
||||
@@ -245,7 +245,7 @@ const handleBulkImport = async () => {
|
||||
importHeight === null ||
|
||||
importVersion === null
|
||||
"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="handleImport()"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-import </v-icon>
|
||||
@@ -260,7 +260,7 @@ const handleBulkImport = async () => {
|
||||
<v-btn
|
||||
color="buttonActive"
|
||||
class="justify-center"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showBulkImportDialog = true)"
|
||||
>
|
||||
<v-icon start class="open-icon"> mdi-import </v-icon>
|
||||
@@ -278,7 +278,7 @@ const handleBulkImport = async () => {
|
||||
color="buttonActive"
|
||||
width="100%"
|
||||
:disabled="importFile === null"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="handleBulkImport()"
|
||||
>
|
||||
<v-icon start class="open-icon" size="large"> mdi-import </v-icon>
|
||||
@@ -292,7 +292,7 @@ const handleBulkImport = async () => {
|
||||
<v-col cols="12" sm="6">
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openExportPrompt"
|
||||
>
|
||||
<v-icon start class="open-icon"> mdi-export </v-icon>
|
||||
@@ -309,7 +309,7 @@ const handleBulkImport = async () => {
|
||||
<v-col cols="12" sm="6">
|
||||
<v-btn
|
||||
color="error"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showNukeDialog = true)"
|
||||
>
|
||||
<v-icon left class="open-icon"> mdi-trash </v-icon>
|
||||
@@ -339,7 +339,7 @@ const handleBulkImport = async () => {
|
||||
small
|
||||
color="error"
|
||||
title="Delete Model"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (confirmDeleteDialog = { show: true, model })"
|
||||
>
|
||||
<v-icon size="large">mdi-trash-can-outline</v-icon>
|
||||
@@ -351,7 +351,7 @@ const handleBulkImport = async () => {
|
||||
small
|
||||
color="buttonActive"
|
||||
title="Rename Model"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showRenameDialog = { show: true, model, newName: '' })"
|
||||
>
|
||||
<v-icon size="large">mdi-pencil</v-icon>
|
||||
@@ -362,7 +362,7 @@ const handleBulkImport = async () => {
|
||||
icon
|
||||
small
|
||||
color="buttonPassive"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="() => (showInfo = { show: true, model })"
|
||||
>
|
||||
<v-icon size="large">mdi-information</v-icon>
|
||||
@@ -391,13 +391,13 @@ const handleBulkImport = async () => {
|
||||
</div>
|
||||
<v-card-actions class="pt-5 pb-0 pr-0" style="justify-content: flex-end">
|
||||
<v-btn
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
color="error"
|
||||
@click="showRenameDialog.show = false"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
color="buttonActive"
|
||||
@click="renameModel(showRenameDialog.model, showRenameDialog.newName)"
|
||||
>Rename</v-btn
|
||||
@@ -413,7 +413,7 @@ const handleBulkImport = async () => {
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
width="100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="openExportIndividualModelPrompt"
|
||||
>
|
||||
<v-icon left class="open-icon" size="large"> mdi-export </v-icon>
|
||||
@@ -446,8 +446,8 @@ const handleBulkImport = async () => {
|
||||
:on-backup="openExportPrompt"
|
||||
:on-confirm="nukeModels"
|
||||
title="Delete and Reset All Object Detection Models"
|
||||
:description="'This will delete ALL object detection models and re-extract the default object detection models. This action cannot be undone.'"
|
||||
:expected-confirmation-text="'Delete Models'"
|
||||
description="This will delete ALL object detection models and re-extract the default object detection models. This action cannot be undone."
|
||||
expected-confirmation-text="Delete Models"
|
||||
delete-text="Delete all models"
|
||||
/>
|
||||
</v-card>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type ThemeInstance } from "vuetify";
|
||||
import { LightTheme, DarkTheme } from "@/plugins/vuetify";
|
||||
|
||||
export const resetTheme = (theme: ThemeInstance) => {
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
const themeType = theme.global.current.value.dark ? "dark" : "light";
|
||||
localStorage.removeItem(`${themeType}-background`);
|
||||
localStorage.removeItem(`${themeType}-primary`);
|
||||
localStorage.removeItem(`${themeType}-secondary`);
|
||||
@@ -12,13 +12,13 @@ export const resetTheme = (theme: ThemeInstance) => {
|
||||
};
|
||||
|
||||
export const getThemeColor = (theme: ThemeInstance, color: string): string => {
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
const defaultTheme = theme.global.name.value === "LightTheme" ? LightTheme : DarkTheme;
|
||||
const themeType = theme.global.current.value.dark ? "dark" : "light";
|
||||
const defaultTheme = theme.global.current.value.dark ? DarkTheme : LightTheme;
|
||||
return localStorage.getItem(`${themeType}-${color}`) ?? defaultTheme.colors![color]!;
|
||||
};
|
||||
|
||||
export const setThemeColor = (theme: ThemeInstance, color: string, value: string | null) => {
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
const themeType = theme.global.current.value.dark ? "dark" : "light";
|
||||
if (value) localStorage.setItem(`${themeType}-${color}`, value);
|
||||
else localStorage.removeItem(`${themeType}-${color}`);
|
||||
|
||||
@@ -38,7 +38,7 @@ export const restoreThemeConfig = (theme: ThemeInstance) => {
|
||||
if (storedTheme) theme.global.name.value = storedTheme;
|
||||
|
||||
// Restore custom theme colors
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
const themeType = theme.global.current.value.dark ? "dark" : "light";
|
||||
const defaultTheme = theme.global.name.value === "LightTheme" ? LightTheme : DarkTheme;
|
||||
|
||||
const customBackground = localStorage.getItem(`${themeType}-background`);
|
||||
@@ -47,7 +47,7 @@ export const restoreThemeConfig = (theme: ThemeInstance) => {
|
||||
const customSurface = localStorage.getItem(`${themeType}-surface`);
|
||||
|
||||
theme.themes.value[theme.global.name.value].colors.background = customBackground ?? defaultTheme.colors!.background!;
|
||||
theme.themes.value[theme.global.name.value].colors.sidebar = theme.themes.value[theme.global.name.value].dark
|
||||
theme.themes.value[theme.global.name.value].colors.sidebar = theme.global.current.value.dark
|
||||
? (customBackground ?? defaultTheme.colors!.sidebar!)
|
||||
: (customSurface ?? defaultTheme.colors!.sidebar!);
|
||||
|
||||
|
||||
@@ -71,10 +71,10 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
|
||||
return this.currentCameraSettings.currentPipelineIndex === WebsocketPipelineType.DriverMode;
|
||||
},
|
||||
isCalibrationMode(): boolean {
|
||||
return this.currentCameraSettings.currentPipelineIndex == WebsocketPipelineType.Calib3d;
|
||||
return this.currentCameraSettings.currentPipelineIndex === WebsocketPipelineType.Calib3d;
|
||||
},
|
||||
isFocusMode(): boolean {
|
||||
return this.currentCameraSettings.currentPipelineIndex == WebsocketPipelineType.FocusCamera;
|
||||
return this.currentCameraSettings.currentPipelineIndex === WebsocketPipelineType.FocusCamera;
|
||||
},
|
||||
isCSICamera(): boolean {
|
||||
return this.currentCameraSettings.isCSICamera;
|
||||
|
||||
@@ -232,7 +232,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="
|
||||
setCameraView(
|
||||
module.matchedCameraInfo,
|
||||
@@ -248,7 +248,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
class="text-black"
|
||||
color="buttonActive"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:loading="deactivatingModule"
|
||||
@click="deactivateModule(module.uniqueName)"
|
||||
>
|
||||
@@ -261,7 +261,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
color="error"
|
||||
style="width: 100%"
|
||||
:loading="module.uniqueName === deletingCamera"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="
|
||||
() =>
|
||||
(confirmDeleteDialog = {
|
||||
@@ -326,7 +326,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="
|
||||
setCameraView(
|
||||
module.matchedCameraInfo,
|
||||
@@ -342,7 +342,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
class="text-black"
|
||||
color="buttonActive"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
:loading="activatingModule"
|
||||
@click="activateModule(module.uniqueName)"
|
||||
>
|
||||
@@ -355,7 +355,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
color="error"
|
||||
style="width: 100%"
|
||||
:loading="module.uniqueName === deletingCamera"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="
|
||||
() =>
|
||||
(confirmDeleteDialog = {
|
||||
@@ -393,7 +393,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
style="width: 100%"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="setCameraView(camera, false)"
|
||||
>
|
||||
<span>Details</span>
|
||||
@@ -405,7 +405,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
color="buttonActive"
|
||||
style="width: 100%"
|
||||
:loading="assigningCamera"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
:variant="theme.global.current.value.dark ? 'outlined' : 'elevated'"
|
||||
@click="assignCamera(camera)"
|
||||
>
|
||||
Activate
|
||||
@@ -457,7 +457,7 @@ const getMatchedDevice = (info: PVCameraInfo | undefined): PVCameraInfo => {
|
||||
density="compact"
|
||||
text="A different camera may have been connected to this device! Compare the following information carefully."
|
||||
icon="mdi-information-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
/>
|
||||
<PvCameraMatchCard :saved="viewingCamera[0]" :current="getMatchedDevice(viewingCamera[0])" />
|
||||
</v-card-text>
|
||||
|
||||
@@ -92,7 +92,7 @@ const showCameraSetupDialog = ref(useCameraSettingsStore().needsCameraConfigurat
|
||||
color="error"
|
||||
density="compact"
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
>
|
||||
<span>
|
||||
Arducam camera detected! Please configure the camera model in the <a href="#/cameras">Camera tab</a>!
|
||||
@@ -104,7 +104,7 @@ const showCameraSetupDialog = ref(useCameraSettingsStore().needsCameraConfigurat
|
||||
color="error"
|
||||
density="compact"
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
>
|
||||
<span>
|
||||
Conflicting hostname detected! Please change the hostname in the <a href="#/settings">Settings tab</a>!
|
||||
@@ -116,7 +116,7 @@ const showCameraSetupDialog = ref(useCameraSettingsStore().needsCameraConfigurat
|
||||
color="error"
|
||||
density="compact"
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
>
|
||||
<span
|
||||
>One or more cameras have an FPS limit set! This may cause performance issues. Check your logs for more
|
||||
@@ -129,7 +129,7 @@ const showCameraSetupDialog = ref(useCameraSettingsStore().needsCameraConfigurat
|
||||
color="error"
|
||||
density="compact"
|
||||
icon="mdi-alert-circle-outline"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'tonal'"
|
||||
:variant="theme.global.current.value.dark ? 'tonal' : 'elevated'"
|
||||
>
|
||||
<span
|
||||
>Conflicting camera name(s) detected! Please change the name(s) of
|
||||
|
||||
@@ -25,7 +25,7 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
rolldownOptions: {
|
||||
external: ["html2canvas", "dompurify", "canvg"]
|
||||
},
|
||||
sourcemap: true
|
||||
|
||||
@@ -23,8 +23,6 @@ import java.util.ArrayList;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class HardwareConfig {
|
||||
public final String deviceName;
|
||||
public final String deviceLogoPath;
|
||||
public final String supportURL;
|
||||
|
||||
// LED control
|
||||
public final ArrayList<Integer> ledPins;
|
||||
@@ -47,8 +45,6 @@ public class HardwareConfig {
|
||||
|
||||
public HardwareConfig(
|
||||
String deviceName,
|
||||
String deviceLogoPath,
|
||||
String supportURL,
|
||||
ArrayList<Integer> ledPins,
|
||||
boolean ledsCanDim,
|
||||
ArrayList<Integer> ledBrightnessRange,
|
||||
@@ -63,8 +59,6 @@ public class HardwareConfig {
|
||||
String restartHardwareCommand,
|
||||
double vendorFOV) {
|
||||
this.deviceName = deviceName;
|
||||
this.deviceLogoPath = deviceLogoPath;
|
||||
this.supportURL = supportURL;
|
||||
this.ledPins = ledPins;
|
||||
this.ledsCanDim = ledsCanDim;
|
||||
this.ledBrightnessRange = ledBrightnessRange;
|
||||
@@ -82,8 +76,6 @@ public class HardwareConfig {
|
||||
|
||||
public HardwareConfig() {
|
||||
deviceName = "";
|
||||
deviceLogoPath = "";
|
||||
supportURL = "";
|
||||
ledPins = new ArrayList<>();
|
||||
ledsCanDim = false;
|
||||
ledBrightnessRange = new ArrayList<>();
|
||||
@@ -121,10 +113,6 @@ public class HardwareConfig {
|
||||
public String toString() {
|
||||
return "HardwareConfig[deviceName="
|
||||
+ deviceName
|
||||
+ ", deviceLogoPath="
|
||||
+ deviceLogoPath
|
||||
+ ", supportURL="
|
||||
+ supportURL
|
||||
+ ", ledPins="
|
||||
+ ledPins
|
||||
+ ", ledsCanDim="
|
||||
|
||||
@@ -104,7 +104,6 @@ public class NetworkTablesManager {
|
||||
|
||||
public void registerTimedTasks() {
|
||||
m_timeSync.start();
|
||||
TimedTaskManager.getInstance().addTask("NTManager", this::ntTick, 5000);
|
||||
TimedTaskManager.getInstance()
|
||||
.addTask("CheckHostnameAndCameraNames", this::checkHostnameAndCameraNames, 10000);
|
||||
}
|
||||
@@ -380,23 +379,6 @@ public class NetworkTablesManager {
|
||||
broadcastVersion();
|
||||
}
|
||||
|
||||
// So it seems like if Photon starts before the robot NT server does, and both aren't static IP,
|
||||
// it'll never connect. This hack works around it by restarting the client/server while the nt
|
||||
// instance isn't connected, same as clicking the save button in the settings menu (or restarting
|
||||
// the service)
|
||||
private void ntTick() {
|
||||
if (!ntInstance.isConnected()
|
||||
&& !ConfigManager.getInstance().getConfig().getNetworkConfig().runNTServer) {
|
||||
setConfig(ConfigManager.getInstance().getConfig().getNetworkConfig());
|
||||
}
|
||||
|
||||
if (!ntInstance.isConnected() && !m_isRetryingConnection) {
|
||||
m_isRetryingConnection = true;
|
||||
logger.error(
|
||||
"[NetworkTablesManager] Could not connect to the robot! Will retry in the background...");
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimeSinceLastPong() {
|
||||
return m_timeSync.getTimeSinceLastPong();
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ public class SystemMonitor {
|
||||
/**
|
||||
* Returns the total GPU memory in MiB.
|
||||
*
|
||||
* @return The total GPU memory in MiB, or -1.0 if not avaialable on this platform.
|
||||
* @return The total GPU memory in MiB, or -1.0 if not available on this platform.
|
||||
*/
|
||||
public double getGpuMem() {
|
||||
return -1.0;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
package org.photonvision.vision.camera;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
|
||||
public enum CameraQuirk {
|
||||
/** Camera settable for controllable image gain */
|
||||
Gain,
|
||||
@@ -29,7 +27,6 @@ public enum CameraQuirk {
|
||||
/** Cap at 100FPS for high-bandwidth cameras */
|
||||
FPSCap100,
|
||||
/** Separate red/blue gain controls available */
|
||||
@JsonAlias("AWBGain") // remove after https://github.com/PhotonVision/photonvision/issues/1488
|
||||
AwbRedBlueGain,
|
||||
/** Will not work with photonvision - Logitech C270 at least */
|
||||
CompletelyBroken,
|
||||
@@ -43,11 +40,8 @@ public enum CameraQuirk {
|
||||
* Camera is an arducam USB ov9281 which has a funky exposure issue where it is defined in v4l as
|
||||
* 1-5000 instead of 1-75
|
||||
*/
|
||||
@JsonAlias("ArduOV9281") // remove after https://github.com/PhotonVision/photonvision/issues/1488
|
||||
ArduOV9281Controls,
|
||||
/** Dummy quirk to tell OV2311 from OV9281 */
|
||||
// from 2024.3.1
|
||||
@JsonAlias("ArduOV2311") // remove after https://github.com/PhotonVision/photonvision/issues/1488
|
||||
ArduOV2311Controls,
|
||||
ArduOV9782Controls,
|
||||
/**
|
||||
|
||||
@@ -170,10 +170,12 @@ public class GenericUSBCameraSettables extends VisionSourceSettables {
|
||||
@Override
|
||||
public void setAutoExposure(boolean cameraAutoExposure) {
|
||||
if ((configuration.cameraQuirks.hasQuirk(CameraQuirk.ArduOV9281Controls)
|
||||
|| configuration.cameraQuirks.hasQuirk(CameraQuirk.ArduOV9782Controls))
|
||||
|| configuration.cameraQuirks.hasQuirk(CameraQuirk.ArduOV9782Controls)
|
||||
|| configuration.cameraQuirks.hasQuirk(CameraQuirk.ArduOV2311Controls))
|
||||
&& !cameraAutoExposure) {
|
||||
// OV9281 and OV9782 on Linux seems to sometimes ignore our exposure requests on first boot if
|
||||
// we're in manual mode. Poking the camera into and out of auto exposure seems to fix it.
|
||||
// OV9281, OV9782, and OV2311 on Linux seems to sometimes ignore our exposure requests on
|
||||
// first boot if we're in manual mode. Poking the camera into and out of auto exposure seems
|
||||
// to fix it.
|
||||
try {
|
||||
setAutoExposureImpl(false);
|
||||
Thread.sleep(2000);
|
||||
|
||||
@@ -19,8 +19,8 @@ package org.photonvision.vision.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.lang.ref.Cleaner.Cleanable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
@@ -36,8 +36,11 @@ public class RknnObjectDetector implements ObjectDetector {
|
||||
/** Cleaner instance to release the detector when it goes out of scope */
|
||||
private final Cleaner cleaner = Cleaner.create();
|
||||
|
||||
/** Atomic boolean to ensure that the native object can only be released once. */
|
||||
private AtomicBoolean released = new AtomicBoolean(false);
|
||||
private final Cleanable cleanable;
|
||||
|
||||
private static Runnable cleanupAction(long ptr) {
|
||||
return () -> RknnJNI.destroy(ptr);
|
||||
}
|
||||
|
||||
/** Pointer to the native object */
|
||||
private final long objPointer;
|
||||
@@ -80,7 +83,7 @@ public class RknnObjectDetector implements ObjectDetector {
|
||||
logger.debug("Created detector for model " + model.modelFile.getName());
|
||||
|
||||
// Register the cleaner to release the detector when it goes out of scope
|
||||
cleaner.register(this, this::release);
|
||||
cleanable = cleaner.register(this, cleanupAction(objPointer));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,17 +140,6 @@ public class RknnObjectDetector implements ObjectDetector {
|
||||
/** Thread-safe method to release the detector. */
|
||||
@Override
|
||||
public void release() {
|
||||
// Checks if the atomic is 'false', and if so, sets it to 'true'
|
||||
if (released.compareAndSet(false, true)) {
|
||||
if (objPointer <= 0) {
|
||||
logger.error(
|
||||
"Detector is not initialized, and so it can't be released! Model: "
|
||||
+ model.modelFile.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
RknnJNI.destroy(objPointer);
|
||||
logger.debug("Released detector for model " + model.modelFile.getName());
|
||||
}
|
||||
cleanable.clean();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package org.photonvision.vision.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.lang.ref.Cleaner.Cleanable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
@@ -36,8 +36,11 @@ public class RubikObjectDetector implements ObjectDetector {
|
||||
/** Cleaner instance to release the detector when it goes out of scope */
|
||||
private final Cleaner cleaner = Cleaner.create();
|
||||
|
||||
/** Atomic boolean to ensure that the native object can only be released once. */
|
||||
private AtomicBoolean released = new AtomicBoolean(false);
|
||||
private final Cleanable cleanable;
|
||||
|
||||
private static Runnable cleanupAction(long ptr) {
|
||||
return () -> RubikJNI.destroy(ptr);
|
||||
}
|
||||
|
||||
/** Pointer to the native object */
|
||||
private final long ptr;
|
||||
@@ -88,7 +91,7 @@ public class RubikObjectDetector implements ObjectDetector {
|
||||
logger.debug("Created detector for model " + model.modelFile.getName());
|
||||
|
||||
// Register the cleaner to release the detector when it goes out of scope
|
||||
cleaner.register(this, this::release);
|
||||
cleanable = cleaner.register(this, cleanupAction(ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,17 +149,7 @@ public class RubikObjectDetector implements ObjectDetector {
|
||||
/** Thread-safe method to release the detector. */
|
||||
@Override
|
||||
public void release() {
|
||||
// Checks if the atomic is 'false', and if so, sets it to 'true'
|
||||
if (released.compareAndSet(false, true)) {
|
||||
if (!isValid()) {
|
||||
logger.error(
|
||||
"Detector is not initialized, and so it can't be released! Model: "
|
||||
+ model.modelFile.getName());
|
||||
return;
|
||||
}
|
||||
RubikJNI.destroy(ptr);
|
||||
logger.debug("Released detector for model " + model.modelFile.getName());
|
||||
}
|
||||
cleanable.clean();
|
||||
}
|
||||
|
||||
private boolean isValid() {
|
||||
|
||||
@@ -150,7 +150,7 @@ public class OutputStreamPipeline {
|
||||
if (!(settings instanceof AprilTagPipelineSettings)
|
||||
&& !(settings instanceof ArucoPipelineSettings)
|
||||
&& !(settings instanceof Calibration3dPipelineSettings)) {
|
||||
// If we're processing anything other than Apriltags..
|
||||
// If we're processing anything other than AprilTags or Aruco targets
|
||||
var draw2dCrosshairResultOnOutput = draw2dCrosshairPipe.run(Pair.of(outMat, targetsToDraw));
|
||||
sumPipeNanosElapsed += pipeProfileNanos[4] = draw2dCrosshairResultOnOutput.nanosElapsed;
|
||||
|
||||
@@ -203,7 +203,7 @@ public class OutputStreamPipeline {
|
||||
}
|
||||
} else if (settings instanceof ArucoPipelineSettings) {
|
||||
if (settings.solvePNPEnabled) {
|
||||
// Draw 3d Apriltag markers (camera is calibrated and running in 3d mode)
|
||||
// Draw 3d aruco markers (camera is calibrated and running in 3d mode)
|
||||
pipeProfileNanos[5] = 0;
|
||||
pipeProfileNanos[6] = 0;
|
||||
|
||||
@@ -213,7 +213,7 @@ public class OutputStreamPipeline {
|
||||
pipeProfileNanos[8] = 0;
|
||||
|
||||
} else {
|
||||
// Draw 2d apriltag markers
|
||||
// Draw 2d aruco markers
|
||||
var draw2dTargetsOnInput = draw2dArucoPipe.run(Pair.of(outMat, targetsToDraw));
|
||||
sumPipeNanosElapsed += pipeProfileNanos[5] = draw2dTargetsOnInput.nanosElapsed;
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@ public class HardwareConfigTest {
|
||||
var config =
|
||||
new ObjectMapper().readValue(TestUtils.getHardwareConfigJson(), HardwareConfig.class);
|
||||
assertEquals(config.deviceName, "PhotonVision");
|
||||
assertEquals(config.deviceLogoPath, "photonvision.png");
|
||||
assertEquals(config.supportURL, "https://support.photonvision.com");
|
||||
// Ensure defaults are not null
|
||||
assertArrayEquals(config.ledPins.stream().mapToInt(i -> i).toArray(), new int[] {2, 13});
|
||||
NativeDeviceFactoryInterface deviceFactory = HardwareManager.configureCustomGPIO(config);
|
||||
|
||||
@@ -368,6 +368,15 @@ class PhotonCameraSim:
|
||||
noisyTargetCorners,
|
||||
)
|
||||
|
||||
# Compute object detection confidence if this is an obj det target
|
||||
classId = tgt.objDetClassId
|
||||
conf = tgt.objDetConf
|
||||
if classId >= 0 and conf < 0:
|
||||
# Simulate confidence using sqrt-scaled area for a more realistic
|
||||
# curve. Raw areaPercent/100 is tiny for most targets; sqrt scaling
|
||||
# gives reasonable values even for small-but-visible objects.
|
||||
conf = max(0.0, min(1.0, math.sqrt(areaPercent / 100.0) * 2.0))
|
||||
|
||||
smallVec: list[TargetCorner] = []
|
||||
for corner in minAreaRectPts:
|
||||
smallVec.append(TargetCorner(corner[0], corner[1]))
|
||||
@@ -381,6 +390,8 @@ class PhotonCameraSim:
|
||||
area=areaPercent,
|
||||
skew=math.degrees(centerRot.X()),
|
||||
fiducialId=tgt.fiducialId,
|
||||
objDetectId=classId,
|
||||
objDetectConf=conf,
|
||||
detectedCorners=cornersFloat,
|
||||
minAreaRectCorners=smallVec,
|
||||
bestCameraToTarget=pnpSim.best if pnpSim else Transform3d(),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import math
|
||||
from typing import overload
|
||||
|
||||
from wpimath.geometry import Pose3d, Translation3d
|
||||
|
||||
@@ -8,19 +9,78 @@ from ..estimation.targetModel import TargetModel
|
||||
class VisionTargetSim:
|
||||
"""Describes a vision target located somewhere on the field that your vision system can detect."""
|
||||
|
||||
def __init__(self, pose: Pose3d, model: TargetModel, id: int = -1):
|
||||
"""Describes a fiducial tag located somewhere on the field that your vision system can detect.
|
||||
@overload
|
||||
def __init__(self, pose: Pose3d, model: TargetModel) -> None:
|
||||
"""
|
||||
Describes a retro-reflective/colored shape vision target located somewhere on the field that
|
||||
your vision system can detect.
|
||||
|
||||
:param pose: Pose3d of the target in field-relative coordinates
|
||||
:param model: TargetModel which describes the shape of the target
|
||||
"""
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, pose: Pose3d, model: TargetModel, id: int) -> None:
|
||||
"""
|
||||
Describes a fiducial tag located somewhere on the field that your vision system can detect.
|
||||
|
||||
:param pose: Pose3d of the tag in field-relative coordinates
|
||||
:param model: TargetModel which describes the shape of the target(tag)
|
||||
:param model: TargetModel which describes the geometry of the target (tag)
|
||||
:param id: The ID of this fiducial tag
|
||||
"""
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self, pose: Pose3d, model: TargetModel, objDetClassId: int, objDetConf: float
|
||||
) -> None:
|
||||
"""
|
||||
Describes an object-detection vision target located somewhere on the field that your vision
|
||||
system can detect.
|
||||
|
||||
:param pose: Pose3d of the target in field-relative coordinates
|
||||
:param model: TargetModel which describes the shape of the target
|
||||
:param objDetClassId: The object detection class ID, or -1 to exclude from object detection
|
||||
:param objDetConf: The object detection confidence, or -1.0 to compute from target area
|
||||
in the camera's field of view
|
||||
"""
|
||||
...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
pose: Pose3d,
|
||||
model: TargetModel,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
if kwargs:
|
||||
raise TypeError(
|
||||
f"VisionTargetSim does not accept keyword arguments: {list(kwargs.keys())}"
|
||||
)
|
||||
|
||||
self.pose: Pose3d = pose
|
||||
self.model: TargetModel = model
|
||||
self.fiducialId: int = id
|
||||
self.objDetClassId: int = -1
|
||||
self.objDetConf: float = -1.0
|
||||
|
||||
if len(args) == 0:
|
||||
# VisionTargetSim(pose, model)
|
||||
self.fiducialId: int = -1
|
||||
self.objDetClassId: int = -1
|
||||
self.objDetConf: float = -1.0
|
||||
elif len(args) == 1:
|
||||
# VisionTargetSim(pose, model, id)
|
||||
self.fiducialId = args[0]
|
||||
self.objDetClassId = -1
|
||||
self.objDetConf = -1.0
|
||||
elif len(args) == 2:
|
||||
# VisionTargetSim(pose, model, objDetClassId, objDetConf)
|
||||
self.fiducialId = -1
|
||||
self.objDetClassId = args[0]
|
||||
self.objDetConf = args[1]
|
||||
else:
|
||||
raise ValueError(
|
||||
f"VisionTargetSim takes 2-4 arguments, got {2 + len(args)}"
|
||||
)
|
||||
|
||||
def __lt__(self, right) -> bool:
|
||||
return self.pose.translation().norm() < right.pose.translation().norm()
|
||||
|
||||
@@ -525,6 +525,16 @@ public class PhotonCameraSim implements AutoCloseable {
|
||||
.get();
|
||||
}
|
||||
|
||||
// If object detection (user classId valid) but conf wasn't provided, estimate
|
||||
int classId = tgt.objDetClassId;
|
||||
float conf = tgt.objDetConf;
|
||||
if (classId >= 0 && conf < 0) {
|
||||
// Simulate confidence using sqrt-scaled area for a more realistic
|
||||
// curve. Raw areaPercent/100 is tiny for most targets; sqrt scaling
|
||||
// gives reasonable values even for small-but-visible objects.
|
||||
conf = (float) MathUtil.clamp(Math.sqrt(areaPercent / 100.0) * 2.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
detectableTgts.add(
|
||||
new PhotonTrackedTarget(
|
||||
-Math.toDegrees(centerRot.getZ()),
|
||||
@@ -532,8 +542,8 @@ public class PhotonCameraSim implements AutoCloseable {
|
||||
areaPercent,
|
||||
Math.toDegrees(centerRot.getX()),
|
||||
tgt.fiducialID,
|
||||
-1,
|
||||
-1,
|
||||
classId,
|
||||
conf,
|
||||
pnpSim.best,
|
||||
pnpSim.alt,
|
||||
pnpSim.ambiguity,
|
||||
|
||||
@@ -36,16 +36,21 @@ public class VisionTargetSim {
|
||||
|
||||
public final int fiducialID;
|
||||
|
||||
/** The object detection class ID, or -1 if not applicable. */
|
||||
public final int objDetClassId;
|
||||
|
||||
/** The object detection confidence, or -1 if not applicable. */
|
||||
public final float objDetConf;
|
||||
|
||||
/**
|
||||
* Describes a vision target located somewhere on the field that your vision system can detect.
|
||||
* Describes a retro-reflective/colored shape vision target located somewhere on the field that
|
||||
* your vision system can detect.
|
||||
*
|
||||
* @param pose Pose3d of the tag in field-relative coordinates
|
||||
* @param model TargetModel which describes the geometry of the target
|
||||
*/
|
||||
public VisionTargetSim(Pose3d pose, TargetModel model) {
|
||||
this.pose = pose;
|
||||
this.model = model;
|
||||
this.fiducialID = -1;
|
||||
this(pose, model, -1, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,9 +61,33 @@ public class VisionTargetSim {
|
||||
* @param id The ID of this fiducial tag
|
||||
*/
|
||||
public VisionTargetSim(Pose3d pose, TargetModel model, int id) {
|
||||
this(pose, model, id, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an object-detection vision target located somewhere on the field that your vision
|
||||
* system can detect. Class ID is the (zero-indexed) index of the object's class ID in the list of
|
||||
* all classes. Confidence can be specified, or pass -1 to estimate confidence based on 2 *
|
||||
* sqrt(target area / total image area)
|
||||
*
|
||||
* @param pose Pose3d of the target in field-relative coordinates
|
||||
* @param model TargetModel which describes the geometry of the target
|
||||
* @param objDetClassId The object detection class ID, if -1 it will not be detected by object
|
||||
* detection
|
||||
* @param objDetConf The object detection confidence, or -1 in which case the simulation will
|
||||
* compute a confidence based on the area of the target in the camera's field of view
|
||||
*/
|
||||
public VisionTargetSim(Pose3d pose, TargetModel model, int objDetClassId, float objDetConf) {
|
||||
this(pose, model, -1, objDetClassId, objDetConf);
|
||||
}
|
||||
|
||||
private VisionTargetSim(
|
||||
Pose3d pose, TargetModel model, int id, int objDetClassId, float objDetConf) {
|
||||
this.pose = pose;
|
||||
this.model = model;
|
||||
this.fiducialID = id;
|
||||
this.objDetClassId = objDetClassId;
|
||||
this.objDetConf = objDetConf;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +126,11 @@ public class VisionTargetSim {
|
||||
return model;
|
||||
}
|
||||
|
||||
/** This target's vertices offset from its field pose. */
|
||||
/**
|
||||
* This target's vertices offset from its field pose.
|
||||
*
|
||||
* @return A vector of Translation3d representing the vertices of the target
|
||||
*/
|
||||
public List<Translation3d> getFieldVertices() {
|
||||
return model.getFieldVertices(pose);
|
||||
}
|
||||
|
||||
@@ -35,11 +35,10 @@
|
||||
#include <hal/FRCUsageReporting.h>
|
||||
#include <net/TimeSyncServer.h>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/core/mat.hpp>
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "PhotonVersion.h"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "photon/dataflow/structures/Packet.h"
|
||||
|
||||
static constexpr units::second_t WARN_DEBOUNCE_SEC = 5_s;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
#include <frc/apriltag/AprilTagFieldLayout.h>
|
||||
#include <frc/apriltag/AprilTagFields.h>
|
||||
|
||||
#include "photon/estimation/CameraTargetRelation.h"
|
||||
#include "photon/estimation/RotTrlTransform3d.h"
|
||||
#include "photon/estimation/VisionEstimation.h"
|
||||
#include "photon/simulation/VideoSimUtil.h"
|
||||
|
||||
namespace photon {
|
||||
PhotonCameraSim::PhotonCameraSim(PhotonCamera* camera)
|
||||
: PhotonCameraSim(camera, photon::SimCameraProperties::PERFECT_90DEG(),
|
||||
@@ -214,12 +219,23 @@ PhotonPipelineResult PhotonCameraSim::Process(
|
||||
}
|
||||
|
||||
std::optional<photon::PnpResult> pnpSim = std::nullopt;
|
||||
if (tgt.fiducialId >= 0 && tgt.GetFieldVertices().size() == 4) {
|
||||
if (tgt.GetFiducialId() >= 0 && tgt.GetFieldVertices().size() == 4) {
|
||||
pnpSim = OpenCVHelp::SolvePNP_Square(
|
||||
prop.GetIntrinsics(), prop.GetDistCoeffs(),
|
||||
tgt.GetModel().GetVertices(), noisyTargetCorners);
|
||||
}
|
||||
|
||||
// Compute object detection confidence if this is an obj det target
|
||||
int classId = tgt.GetObjDetClassId();
|
||||
float conf = tgt.GetObjDetConf();
|
||||
if (classId >= 0 && conf < 0) {
|
||||
// Simulate confidence using sqrt-scaled area for a more realistic
|
||||
// curve. Raw areaPercent/100 is tiny for most targets; sqrt scaling
|
||||
// gives reasonable values even for small-but-visible objects.
|
||||
conf = static_cast<float>(
|
||||
std::clamp(std::sqrt(areaPercent / 100.0) * 2.0, 0.0, 1.0));
|
||||
}
|
||||
|
||||
std::vector<std::pair<float, float>> tempCorners =
|
||||
OpenCVHelp::PointsToCorners(minAreaRectPts);
|
||||
std::vector<TargetCorner> smallVec;
|
||||
@@ -236,8 +252,8 @@ PhotonPipelineResult PhotonCameraSim::Process(
|
||||
detectableTgts.emplace_back(
|
||||
-centerRot.Z().convert<units::degrees>().to<double>(),
|
||||
-centerRot.Y().convert<units::degrees>().to<double>(), areaPercent,
|
||||
centerRot.X().convert<units::degrees>().to<double>(), tgt.fiducialId,
|
||||
tgt.objDetClassId, tgt.objDetConf,
|
||||
centerRot.X().convert<units::degrees>().to<double>(),
|
||||
tgt.GetFiducialId(), classId, conf,
|
||||
pnpSim ? pnpSim->best : frc::Transform3d{},
|
||||
pnpSim ? pnpSim->alt : frc::Transform3d{},
|
||||
pnpSim ? pnpSim->ambiguity : -1, smallVec, cornersDouble);
|
||||
@@ -254,8 +270,8 @@ PhotonPipelineResult PhotonCameraSim::Process(
|
||||
VisionTargetSim tgt = pair.first;
|
||||
std::vector<cv::Point2f> corners = pair.second;
|
||||
|
||||
if (tgt.fiducialId > 0) {
|
||||
VideoSimUtil::Warp165h5TagImage(tgt.fiducialId, corners, true,
|
||||
if (tgt.GetFiducialId() > 0) {
|
||||
VideoSimUtil::Warp165h5TagImage(tgt.GetFiducialId(), corners, true,
|
||||
videoSimFrameRaw);
|
||||
} else if (!tgt.GetModel().GetIsSpherical()) {
|
||||
std::vector<cv::Point2f> contour = corners;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <frc/Errors.h>
|
||||
|
||||
using namespace photon;
|
||||
|
||||
void SimCameraProperties::SetCalibration(int width, int height,
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <frc/Alert.h>
|
||||
#include <networktables/BooleanTopic.h>
|
||||
#include <networktables/DoubleArrayTopic.h>
|
||||
#include <networktables/DoubleTopic.h>
|
||||
#include <networktables/IntegerTopic.h>
|
||||
#include <networktables/MultiSubscriber.h>
|
||||
#include <networktables/NetworkTable.h>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <frc/geometry/Rotation3d.h>
|
||||
#include <frc/geometry/Transform3d.h>
|
||||
#include <frc/interpolation/TimeInterpolatableBuffer.h>
|
||||
#include <opencv2/core/mat.hpp>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "photon/PhotonCamera.h"
|
||||
#include "photon/targeting/PhotonPipelineResult.h"
|
||||
|
||||
@@ -26,11 +26,7 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "photon/dataflow/structures/Packet.h"
|
||||
#include "photon/targeting/MultiTargetPNPResult.h"
|
||||
#include "photon/targeting/PhotonPipelineResult.h"
|
||||
#include "photon/targeting/PhotonTrackedTarget.h"
|
||||
#include "photon/targeting/PnpResult.h"
|
||||
|
||||
namespace photon {
|
||||
|
||||
|
||||
@@ -32,12 +32,6 @@
|
||||
#include <units/length.h>
|
||||
#include <units/math.h>
|
||||
|
||||
#include "photon/dataflow/structures/Packet.h"
|
||||
#include "photon/targeting/MultiTargetPNPResult.h"
|
||||
#include "photon/targeting/PhotonPipelineResult.h"
|
||||
#include "photon/targeting/PhotonTrackedTarget.h"
|
||||
#include "photon/targeting/PnpResult.h"
|
||||
|
||||
namespace photon {
|
||||
class PhotonUtils {
|
||||
public:
|
||||
|
||||
@@ -24,23 +24,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cameraserver/CameraServer.h>
|
||||
#include <frc/Timer.h>
|
||||
#include <frc/apriltag/AprilTagFieldLayout.h>
|
||||
#include <frc/apriltag/AprilTagFields.h>
|
||||
#include <photon/PhotonCamera.h>
|
||||
#include <photon/PhotonTargetSortMode.h>
|
||||
#include <photon/estimation/CameraTargetRelation.h>
|
||||
#include <photon/estimation/VisionEstimation.h>
|
||||
#include <photon/networktables/NTTopicSet.h>
|
||||
#include <photon/simulation/SimCameraProperties.h>
|
||||
#include <photon/simulation/VideoSimUtil.h>
|
||||
#include <photon/simulation/VisionTargetSim.h>
|
||||
#include <units/math.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
@@ -24,15 +24,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <frc/Errors.h>
|
||||
#include <frc/MathUtil.h>
|
||||
#include <frc/geometry/Rotation2d.h>
|
||||
#include <frc/geometry/Translation3d.h>
|
||||
#include <photon/estimation/OpenCVHelp.h>
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
#include <opencv2/objdetect.hpp>
|
||||
#include <units/length.h>
|
||||
|
||||
#include "SimCameraProperties.h"
|
||||
#include "photon/estimation/RotTrlTransform3d.h"
|
||||
#include "photon/simulation/SimCameraProperties.h"
|
||||
|
||||
namespace mathutil {
|
||||
template <typename T>
|
||||
|
||||
@@ -346,11 +346,14 @@ class VisionSystemSim {
|
||||
const std::vector<VisionTargetSim>& targets) {
|
||||
std::vector<VisionTargetSim> removedList;
|
||||
for (auto& entry : targetSets) {
|
||||
for (auto target : entry.second) {
|
||||
auto it = std::find(targets.begin(), targets.end(), target);
|
||||
if (it != targets.end()) {
|
||||
removedList.emplace_back(target);
|
||||
entry.second.erase(it);
|
||||
auto& vec = entry.second;
|
||||
auto it = vec.begin();
|
||||
while (it != vec.end()) {
|
||||
if (std::find(targets.begin(), targets.end(), *it) != targets.end()) {
|
||||
removedList.emplace_back(*it);
|
||||
it = vec.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,23 +31,120 @@
|
||||
#include "photon/estimation/TargetModel.h"
|
||||
|
||||
namespace photon {
|
||||
/** Describes a vision target located somewhere on the field that your vision
|
||||
* system can detect. */
|
||||
class VisionTargetSim {
|
||||
public:
|
||||
/**
|
||||
* Describes a retro-reflective/colored shape vision target located somewhere
|
||||
* on the field that your vision system can detect.
|
||||
*
|
||||
* @param pose Pose3d of the tag in field-relative coordinates
|
||||
* @param model TargetModel which describes the geometry of the target
|
||||
*/
|
||||
VisionTargetSim(const frc::Pose3d& pose, const TargetModel& model)
|
||||
: fiducialId(-1), pose(pose), model(model) {}
|
||||
: fiducialId(-1),
|
||||
objDetClassId(-1),
|
||||
objDetConf(-1),
|
||||
pose(pose),
|
||||
model(model) {}
|
||||
|
||||
/**
|
||||
* Describes a fiducial tag located somewhere on the field that your vision
|
||||
* system can detect.
|
||||
*
|
||||
* @param pose Pose3d of the tag in field-relative coordinates
|
||||
* @param model TargetModel which describes the geometry of the target(tag)
|
||||
* @param id The ID of this fiducial tag
|
||||
*/
|
||||
VisionTargetSim(const frc::Pose3d& pose, const TargetModel& model, int id)
|
||||
: fiducialId(id), pose(pose), model(model) {}
|
||||
: fiducialId(id),
|
||||
objDetClassId(-1),
|
||||
objDetConf(-1),
|
||||
pose(pose),
|
||||
model(model) {}
|
||||
|
||||
/**
|
||||
* Describes an object-detection vision target located somewhere on the field
|
||||
* that your vision system can detect. Class ID is the (zero-indexed) index of
|
||||
* the object's class ID in the list of all classes. Confidence can be
|
||||
* specified, or pass -1 to estimate confidence based on 2 * sqrt(target area
|
||||
* / total image area)
|
||||
*
|
||||
* @param pose Pose3d of the target in field-relative coordinates
|
||||
* @param model TargetModel which describes the geometry of the target
|
||||
* @param objDetClassId The object detection class ID, if -1 it will not be
|
||||
* detected by object detection
|
||||
* @param objDetConf The object detection confidence, or -1 in which case the
|
||||
* simulation will compute a confidence based on the area of the target in the
|
||||
* camera's field of view
|
||||
*/
|
||||
VisionTargetSim(const frc::Pose3d& pose, const TargetModel& model,
|
||||
int objDetClassId, float objDetConf)
|
||||
: fiducialId(-1),
|
||||
objDetClassId(objDetClassId),
|
||||
objDetConf(objDetConf),
|
||||
pose(pose),
|
||||
model(model) {}
|
||||
|
||||
/**
|
||||
* Sets the pose of this target on the field.
|
||||
*
|
||||
* @param newPose The pose in field-relative coordinates
|
||||
*/
|
||||
void SetPose(const frc::Pose3d& newPose) { pose = newPose; }
|
||||
|
||||
/**
|
||||
* Sets the model describing this target's geometry.
|
||||
*
|
||||
* @param newModel The model of the target
|
||||
*/
|
||||
void SetModel(const TargetModel& newModel) { model = newModel; }
|
||||
|
||||
/**
|
||||
* Returns the pose of this target on the field.
|
||||
*
|
||||
* @return The pose in field-relative coordinates
|
||||
*/
|
||||
frc::Pose3d GetPose() const { return pose; }
|
||||
|
||||
/**
|
||||
* Returns the model describing this target's geometry.
|
||||
*
|
||||
* @return The model of the target
|
||||
*/
|
||||
TargetModel GetModel() const { return model; }
|
||||
|
||||
/**
|
||||
* Returns the fiducial ID of this target, or -1 if not a fiducial target.
|
||||
*
|
||||
* @return The fiducial ID
|
||||
*/
|
||||
int GetFiducialId() const { return fiducialId; }
|
||||
|
||||
/**
|
||||
* Returns the object detection class ID of this target, or -1 if not an
|
||||
* object detection target.
|
||||
*
|
||||
* @return The object detection class ID
|
||||
*/
|
||||
int GetObjDetClassId() const { return objDetClassId; }
|
||||
|
||||
/**
|
||||
* Returns the object detection confidence of this target, or -1 if
|
||||
* confidence is estimated from target area or is not an object.
|
||||
*
|
||||
* @return The object detection confidence
|
||||
*/
|
||||
float GetObjDetConf() const { return objDetConf; }
|
||||
|
||||
/**
|
||||
* This target's vertices offset from its field pose.
|
||||
* @return A vector of Translation3d representing the vertices of the target
|
||||
*/
|
||||
std::vector<frc::Translation3d> GetFieldVertices() const {
|
||||
return model.GetFieldVertices(pose);
|
||||
}
|
||||
int fiducialId;
|
||||
|
||||
int objDetClassId = -1;
|
||||
float objDetConf = -1;
|
||||
|
||||
bool operator<(const VisionTargetSim& right) const {
|
||||
return pose.Translation().Norm() < right.pose.Translation().Norm();
|
||||
@@ -70,6 +167,9 @@ class VisionTargetSim {
|
||||
}
|
||||
|
||||
private:
|
||||
int fiducialId;
|
||||
int objDetClassId;
|
||||
float objDetConf;
|
||||
frc::Pose3d pose;
|
||||
TargetModel model;
|
||||
};
|
||||
|
||||
@@ -601,7 +601,49 @@ class VisionSystemSimTest {
|
||||
|
||||
robotPose = new Pose2d(-2, -2, Rotation2d.fromDegrees(30));
|
||||
visionSysSim.update(robotPose);
|
||||
ambiguity = waitForSequenceNumber(camera, 2).getBestTarget().getPoseAmbiguity();
|
||||
var target2 = waitForSequenceNumber(camera, 2).getBestTarget();
|
||||
ambiguity = target2.getPoseAmbiguity();
|
||||
assertTrue(0 < ambiguity && ambiguity < 0.2, "Tag ambiguity expected to be low");
|
||||
|
||||
// and prove that object detection class id/conf are -1 when we look at a tag
|
||||
assertEquals(-1, target2.objDetectId);
|
||||
assertEquals(-1, target2.objDetectConf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectDetection() {
|
||||
var visionSysSim = new VisionSystemSim("Test");
|
||||
var camera = new PhotonCamera(inst, "camera");
|
||||
var cameraSim = new PhotonCameraSim(camera);
|
||||
visionSysSim.addCamera(cameraSim, new Transform3d());
|
||||
cameraSim.prop.setCalibration(640, 480, Rotation2d.fromDegrees(80));
|
||||
cameraSim.setMinTargetAreaPixels(20.0);
|
||||
|
||||
final var targetPose = new Pose3d(new Translation3d(2, 0, 0), new Rotation3d(0, 0, Math.PI));
|
||||
final int classId = 3;
|
||||
final float conf = 0.67f;
|
||||
final TargetModel ballModel = new TargetModel(Units.inchesToMeters(6));
|
||||
final var ballTarget = new VisionTargetSim(targetPose, ballModel, classId, conf);
|
||||
|
||||
visionSysSim.addVisionTargets(ballTarget);
|
||||
|
||||
var robotPose = Pose2d.kZero;
|
||||
visionSysSim.update(robotPose);
|
||||
var target1 = waitForSequenceNumber(camera, 1).getBestTarget();
|
||||
assertEquals(classId, target1.objDetectId);
|
||||
assertEquals(conf, target1.objDetectConf);
|
||||
assertEquals(-1, target1.fiducialId);
|
||||
|
||||
// much around with the target to force PhotonCameraSim::process calculate conf
|
||||
visionSysSim.removeVisionTargets(ballTarget);
|
||||
final float conf2 = -1;
|
||||
final var ballTarget2 = new VisionTargetSim(targetPose, ballModel, classId, conf2);
|
||||
visionSysSim.addVisionTargets(ballTarget2);
|
||||
visionSysSim.update(robotPose);
|
||||
var target2 = waitForSequenceNumber(camera, 2).getBestTarget();
|
||||
assertEquals(classId, target2.objDetectId);
|
||||
// 2 * sqrt(area pixels) at this particular pose
|
||||
assertEquals(0.131, target2.objDetectConf, 0.01);
|
||||
assertEquals(-1, target2.fiducialId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "photon/PhotonPoseEstimator.h"
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -22,12 +22,11 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "PhotonVersion.h"
|
||||
|
||||
TEST(VersionTest, PrintVersion) {
|
||||
std::cout << photon::PhotonVersion::versionString << std::endl;
|
||||
wpi::println("{}", photon::PhotonVersion::versionString);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "photon/simulation/VisionSystemSim.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
@@ -33,6 +31,7 @@
|
||||
#include <wpi/deprecated.h>
|
||||
|
||||
#include "photon/PhotonUtils.h"
|
||||
#include "photon/estimation/VisionEstimation.h"
|
||||
|
||||
// Ignore GetLatestResult warnings
|
||||
WPI_IGNORE_DEPRECATED
|
||||
|
||||
@@ -17,20 +17,14 @@
|
||||
|
||||
#include "net/TimeSyncClient.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/struct/Struct.h>
|
||||
#include <wpinet/UDPClient.h>
|
||||
#include <wpinet/uv/util.h>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
@@ -17,22 +17,18 @@
|
||||
|
||||
#include "net/TimeSyncServer.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/struct/Struct.h>
|
||||
#include <wpinet/UDPClient.h>
|
||||
#include <wpinet/uv/util.h>
|
||||
|
||||
#include "ntcore_cpp.h"
|
||||
#include "net/TimeSyncStructs.h"
|
||||
|
||||
static void ServerLoggerFunc(unsigned int level, const char* file,
|
||||
unsigned int line, const char* msg) {
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
|
||||
#include "photon/constrained_solvepnp/wrap/casadi_wrapper.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Cholesky>
|
||||
#include <Eigen/Core>
|
||||
@@ -192,10 +189,10 @@ constrained_solvepnp::do_optimization(
|
||||
fmt::println("{} tags", nTags);
|
||||
// fmt::println("nstate {}", nState);
|
||||
|
||||
std::cout << "robot2camera:\n" << robot2camera << std::endl;
|
||||
std::cout << "x guess:\n" << x_guess << std::endl;
|
||||
std::cout << "field2pt:\n" << field2points << std::endl;
|
||||
std::cout << "observations:\n" << point_observations << std::endl;
|
||||
fmt::println("robot2camera:\n{}", robot2camera);
|
||||
fmt::println("x guess:\n{}", x_guess);
|
||||
fmt::println("field2pt:\n{}", field2points);
|
||||
fmt::println("observations:\n{}", point_observations);
|
||||
fmt::println("---------^^^^^^^^---------");
|
||||
}
|
||||
|
||||
@@ -252,7 +249,7 @@ constrained_solvepnp::do_optimization(
|
||||
|
||||
auto H_ldlt = H.ldlt();
|
||||
if (H_ldlt.info() != Eigen::Success) {
|
||||
std::cerr << "LDLT decomp failed! H=" << std::endl << H << std::endl;
|
||||
fmt::println(stderr, "LDLT decomp failed! H=\n{}", H);
|
||||
return wpi::unexpected{slp::ExitStatus::LOCALLY_INFEASIBLE};
|
||||
}
|
||||
|
||||
@@ -276,7 +273,7 @@ constrained_solvepnp::do_optimization(
|
||||
H_ldlt = H_reg.ldlt();
|
||||
|
||||
if (H_ldlt.info() != Eigen::Success) {
|
||||
std::cerr << "LDLT decomp failed! H=" << std::endl << H << std::endl;
|
||||
fmt::println(stderr, "LDLT decomp failed! H=\n{}", H);
|
||||
return wpi::unexpected{slp::ExitStatus::LOCALLY_INFEASIBLE};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
|
||||
#include "photon/estimation/VisionEstimation.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "photon/constrained_solvepnp/wrap/casadi_wrapper.h"
|
||||
#include "photon/estimation/OpenCVHelp.h"
|
||||
#include "photon/targeting/MultiTargetPNPResult.h"
|
||||
|
||||
namespace photon {
|
||||
namespace VisionEstimation {
|
||||
|
||||
@@ -17,14 +17,4 @@
|
||||
|
||||
#include "photon/targeting/PhotonTrackedTarget.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <frc/geometry/Translation2d.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
static constexpr const uint8_t MAX_CORNERS = 8;
|
||||
|
||||
namespace photon {} // namespace photon
|
||||
|
||||
@@ -17,31 +17,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <frc/filter/MedianFilter.h>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/static_circular_buffer.h>
|
||||
#include <wpi/struct/Struct.h>
|
||||
#include <wpinet/EventLoopRunner.h>
|
||||
#include <wpinet/UDPClient.h>
|
||||
#include <wpinet/uv/Buffer.h>
|
||||
#include <wpinet/uv/Timer.h>
|
||||
#include <wpinet/uv/Udp.h>
|
||||
|
||||
#include "TimeSyncStructs.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace wpi {
|
||||
namespace tsp {
|
||||
|
||||
@@ -17,30 +17,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/struct/Struct.h>
|
||||
#include <wpinet/EventLoopRunner.h>
|
||||
#include <wpinet/UDPClient.h>
|
||||
#include <wpinet/uv/Buffer.h>
|
||||
#include <wpinet/uv/Timer.h>
|
||||
#include <wpinet/uv/Udp.h>
|
||||
|
||||
#include "TimeSyncStructs.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace wpi {
|
||||
namespace tsp {
|
||||
|
||||
|
||||
@@ -17,17 +17,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/Demangle.h>
|
||||
#include <wpi/ct_string.h>
|
||||
#include <wpi/struct/Struct.h>
|
||||
|
||||
namespace photon {
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#define OPENCV_DISABLE_EIGEN_TENSOR_SUPPORT
|
||||
#include <opencv2/core/eigen.hpp>
|
||||
|
||||
#include "photon/targeting/MultiTargetPNPResult.h"
|
||||
#include "photon/targeting/PnpResult.h"
|
||||
#include "photon/targeting/TargetCorner.h"
|
||||
|
||||
|
||||
@@ -102,7 +102,5 @@ class NTTopicSet {
|
||||
cameraDistortionPublisher =
|
||||
subTable->GetDoubleArrayTopic("cameraDistortion").Publish();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace photon
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <frc/geometry/Transform3d.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "PnpResult.h"
|
||||
#include "photon/dataflow/structures/Packet.h"
|
||||
#include "photon/struct/MultiTargetPNPResultStruct.h"
|
||||
|
||||
namespace photon {
|
||||
|
||||
@@ -18,16 +18,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <units/time.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "MultiTargetPNPResult.h"
|
||||
#include "PhotonTrackedTarget.h"
|
||||
#include "fmt/base.h"
|
||||
#include "photon/dataflow/structures/Packet.h"
|
||||
#include "photon/struct/PhotonPipelineResultStruct.h"
|
||||
|
||||
namespace photon {
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <frc/geometry/Transform3d.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "photon/struct/PhotonTrackedTargetStruct.h"
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <frc/geometry/Transform3d.h>
|
||||
|
||||
#include "photon/dataflow/structures/Packet.h"
|
||||
#include "photon/struct/PnpResultStruct.h"
|
||||
|
||||
namespace photon {
|
||||
|
||||
@@ -15,13 +15,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "org_photonvision_jni_ConstrainedSolvepnpJni.h"
|
||||
#include "photon/constrained_solvepnp/wrap/casadi_wrapper.h"
|
||||
|
||||
@@ -72,18 +68,6 @@ Java_org_photonvision_jni_ConstrainedSolvepnpJni_do_1optimization
|
||||
pointObservationsMat(pointObservationsVec.data(), 2,
|
||||
pointObservationsVec.size() / 2);
|
||||
|
||||
#if 0
|
||||
fmt::println("======================================================");
|
||||
fmt::println("Got robot2camera raw {}", robot2cameraVec);
|
||||
fmt::println("Camera cal {} {} {} {}", cameraCal_.fx, cameraCal_.fy,
|
||||
cameraCal_.cx, cameraCal_.cy);
|
||||
fmt::println("{} tags", nTags);
|
||||
std::cout << "robot2camera:\n" << robot2cameraMat << std::endl;
|
||||
std::cout << "x guess:\n" << xGuessMat << std::endl;
|
||||
std::cout << "field2pt:\n" << field2pointsMat << std::endl;
|
||||
std::cout << "observations:\n" << pointObservationsMat << std::endl;
|
||||
#endif
|
||||
|
||||
wpi::expected<constrained_solvepnp::RobotStateMat, slp::ExitStatus> result =
|
||||
constrained_solvepnp::do_optimization(
|
||||
headingFree, nTags, cameraCal_, robot2cameraMat, xGuessMat,
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <org_photonvision_jni_TimeSyncClient.h>
|
||||
#include <org_photonvision_jni_TimeSyncServer.h>
|
||||
|
||||
|
||||
@@ -15,12 +15,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <frc/fmt/Eigen.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
#include "photon/constrained_solvepnp/wrap/casadi_wrapper.h"
|
||||
@@ -169,11 +166,10 @@ void print_cost(casadi_real robot_x, casadi_real robot_y,
|
||||
x_guess, field2points, point_observations, 0, 0);
|
||||
auto end = wpi::Now();
|
||||
|
||||
std::cout << i << "," << static_cast<bool>(x_out) << "," << end - start
|
||||
<< std::endl;
|
||||
std::cout << "Solution:"
|
||||
<< x_out.value_or(constrained_solvepnp::RobotStateMat::Identity())
|
||||
<< std::endl;
|
||||
wpi::println("{},{},{}", i, static_cast<bool>(x_out), end - start);
|
||||
wpi::println(
|
||||
"Solution: {}",
|
||||
x_out.value_or(constrained_solvepnp::RobotStateMat::Identity()));
|
||||
// std::cout << "iter "
|
||||
// << i
|
||||
// // << "\nGuess:\n" << x_guess << "\n Optimized ->\n"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <hal/HAL.h>
|
||||
#include <net/TimeSyncClient.h>
|
||||
#include <net/TimeSyncServer.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
TEST(TimeSyncProtocolTest, Smoketest) {
|
||||
using namespace wpi::tsp;
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "Robot.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
#include <photon/PhotonUtils.h>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveDrive.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <frc/TimedRobot.h>
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveDriveSim.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/system/Discretization.h>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveModule.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <frc/MathUtil.h>
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "Robot.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveDrive.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <frc/TimedRobot.h>
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveDriveSim.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/system/Discretization.h>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveModule.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <frc/MathUtil.h>
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "Robot.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <frc/simulation/BatterySim.h>
|
||||
#include <frc/simulation/RoboRioSim.h>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveDrive.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <frc/TimedRobot.h>
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveDriveSim.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <frc/RobotController.h>
|
||||
#include <frc/system/Discretization.h>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "subsystems/SwerveModule.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <frc/MathUtil.h>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <regex>
|
||||
|
||||
/*
|
||||
* Autogenerated file! Do not manually edit this file. This version is
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user