Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
71e3bcc
Remove jointsolve contents
yasen5 Feb 14, 2026
6180267
Merge branch 'main' into joint-solve
yasen5 Feb 14, 2026
8b85bb0
Merge branch 'main' into joint-solve
yasen5 Feb 14, 2026
1545a08
progress
yasen5 Feb 14, 2026
e85bb73
Small fix
yasen5 Feb 14, 2026
164a18d
more progress, builds
yasen5 Feb 14, 2026
5ac821e
Unchopify git
yasen5 Feb 16, 2026
e2b351e
Merge branch 'main' into joint-solve
yasen5 Feb 16, 2026
4aad232
Fix json issue
yasen5 Feb 16, 2026
3ffec12
Add matrix utils
yasen5 Feb 16, 2026
a23e3bc
Working with wpilib 2026
yasen5 Feb 16, 2026
e3c7236
Split up square solve functions
yasen5 Feb 16, 2026
808f0df
Small cleanup
yasen5 Feb 16, 2026
356aace
Builds again
yasen5 Feb 17, 2026
d3c77f3
Progress point
yasen5 Feb 18, 2026
0f287d4
Projection slightly offset
yasen5 Feb 18, 2026
8ee3458
Fix ordering of image points in test
yasen5 Feb 18, 2026
8223a51
Square solve weird???
yasen5 Feb 18, 2026
07d3dc4
Merge branch 'main' into joint-solve
yasen5 Feb 19, 2026
7224eba
Bruh
yasen5 Feb 19, 2026
f7c23dd
Progress
yasen5 Feb 19, 2026
2d16f15
Good but stack smashing
yasen5 Feb 20, 2026
6c15074
Fix stack smashing
yasen5 Feb 20, 2026
0205ce3
Add back optional
yasen5 Feb 20, 2026
59e2b12
Math seems good but error is off
yasen5 Feb 20, 2026
0bc31f5
error still too high
yasen5 Feb 20, 2026
beb29b4
Translation
yasen5 Feb 21, 2026
016eb54
Cleanup
yasen5 Feb 21, 2026
4ae4a72
Gpt rotation logic
yasen5 Feb 21, 2026
2561571
Merge branch 'main' into joint-solve
yasen5 Feb 22, 2026
91aa9ea
Get rid of gpt stuff
yasen5 Feb 22, 2026
b820860
Add in utils functions in preparation for backprop
yasen5 Feb 22, 2026
0b7cd8b
Slight refactor
yasen5 Feb 22, 2026
63651b2
Little bit of derivatives but about to switch up setup
yasen5 Feb 22, 2026
2ec85cc
Logic but not working
yasen5 Feb 22, 2026
61be620
Fix projection calculation, still not working
yasen5 Feb 22, 2026
3e28e39
uhhhh how was this merged?
yasen5 Feb 22, 2026
013b251
Works
yasen5 Feb 22, 2026
8eacce2
Progress
yasen5 Mar 1, 2026
3b69eb1
Charlie suggested refactor but I don't think this is the strat
yasen5 Mar 1, 2026
133a36a
Working
yasen5 Mar 1, 2026
f0344db
Cleanup part 1
yasen5 Mar 1, 2026
dab2b42
Refactor other solvers for better testing
yasen5 Mar 1, 2026
1ca03bd
Merge branch 'main' into joint-solve
yasen5 Mar 1, 2026
cadb025
Very slight cleanup
yasen5 Mar 1, 2026
94f698b
Use size_t
yasen5 Mar 1, 2026
d2a7c1c
Add Graham-Schmidt normalizer to deal with floating-point error, work…
yasen5 Mar 1, 2026
12755a5
Merge branch 'main' into joint-solve
yasen5 Mar 1, 2026
0ec2937
Unclean readme
yasen5 Mar 2, 2026
8fd244c
Cleanup readme
yasen5 Mar 2, 2026
e01484f
Can gpt seriously not format a readme
yasen5 Mar 2, 2026
aa80c0f
Merge branch 'main' into joint-solve
yasen5 Mar 3, 2026
61e9204
Fix affineness, extrinsics application, add variance
yasen5 Mar 3, 2026
c1b8a4d
Merge branch 'main' into joint-solve
yasen5 Mar 3, 2026
9484158
use new variance function
yasen5 Mar 3, 2026
595224b
Refactor camera source construction
yasen5 Mar 4, 2026
042efab
Merge branch 'main' into joint-solve
yasen5 Mar 6, 2026
5f6e051
Merge branch 'main' into joint-solve
yasen5 Mar 7, 2026
ddff4b2
Merge branch 'main' into joint-solve
yasen5 Mar 7, 2026
78adc7f
Improve testing
yasen5 Mar 7, 2026
664531a
Add print transform for eigen matrix
yasen5 Mar 8, 2026
041ca3c
Fix printing function
yasen5 Mar 8, 2026
37a958e
Fix ordering of fake detection points
yasen5 Mar 8, 2026
ac4810f
Add yaw only option
yasen5 Mar 8, 2026
d4d12d0
Fix testing
yasen5 Mar 11, 2026
57384fd
Merge branch 'main' into joint-solve
yasen5 Mar 11, 2026
4550f39
Switch to using square solve for initial pose estimate
yasen5 Mar 11, 2026
cbb0443
Add scalar for iterations on open rotation
yasen5 Mar 11, 2026
2c5a346
Progress point
yasen5 Mar 12, 2026
67197c8
Translation bad rotation good
yasen5 Mar 12, 2026
b6849f1
Reduce floating point calculations
yasen5 Mar 12, 2026
ea6d8ad
Merge branch 'main' into joint-solve CHOPPPED FIX IMMEDIATELY
yasen5 Mar 12, 2026
288c791
Revert "Merge branch 'main' into joint-solve CHOPPPED FIX IMMEDIATELY"
yasen5 Mar 13, 2026
ac29e6e
Merge branch 'main' into joint-solve BAD MERGE
yasen5 Mar 13, 2026
2738898
Chopped merge
yasen5 Mar 13, 2026
0183214
THREAD BUILDS
yasen5 Mar 13, 2026
c8c03c1
Merge branch 'main' into joint-solve
yasen5 Mar 14, 2026
d322172
Testing changes
yasen5 Mar 14, 2026
311e779
Sim part 2
yasen5 Mar 14, 2026
88efa64
More progress
yasen5 Mar 14, 2026
a6ca8d6
more progress, stop undistorting twice
yasen5 Mar 14, 2026
8023574
Solver works on tag 26, needs tuning and testing
yasen5 Mar 14, 2026
1540689
Fix extremely stupid typo
yasen5 Mar 14, 2026
6c822b0
More progress
yasen5 Mar 15, 2026
09efab6
Merge branch 'main' into joint-solve
yasen5 Mar 15, 2026
f05ae1c
Small fix
yasen5 Mar 15, 2026
7c2d9f4
Logging works but chopped
yasen5 Mar 15, 2026
10e7842
Publish loss
yasen5 Mar 15, 2026
ca0d56d
I'm finding the person who made this crap logger
yasen5 Mar 15, 2026
072fcf1
Tested on robot
yasen5 Mar 15, 2026
8b7ca73
Logging
yasen5 Mar 15, 2026
2fa6d4f
Fix wpilog viewing
yasen5 Mar 15, 2026
84af4ce
Compare both solves but timestamp is hosed
yasen5 Mar 15, 2026
660588b
Merge branch 'main' into joint-solve
yasen5 Apr 18, 2026
8bfc45d
Add multi camera detector
yasen5 Apr 18, 2026
de3aa6a
Add streaming
yasen5 Apr 18, 2026
292d013
Holy change, builds tho
yasen5 Apr 23, 2026
5f2db9d
Works?
yasen5 Apr 24, 2026
8d6bce7
Merge branch 'main' into unambiguous-refactor
yasen5 Apr 25, 2026
2df055a
Fix invalid pose estimate return and fix testing
yasen5 Apr 25, 2026
e86fb6c
Cleanup
yasen5 Apr 25, 2026
76c5ea8
Revert change to disk camera
yasen5 Apr 25, 2026
53f40ce
Merge branch 'main' into joint-solve
yasen5 Apr 25, 2026
45446cf
Dp not working
yasen5 Apr 25, 2026
51244d5
Log ptr
yasen5 Apr 25, 2026
7d2036e
Remove duplicate multicameradetector which was causing threading issues
yasen5 Apr 26, 2026
1ade23f
Works on dev orin
yasen5 Apr 26, 2026
8ffa73f
Cleanup
yasen5 Apr 26, 2026
c450544
Cleanup p2
yasen5 Apr 27, 2026
b5e0f24
I love optionals
yasen5 Apr 27, 2026
11a4dba
Switch back to real cameras
yasen5 Apr 27, 2026
940a39a
Address comments
yasen5 Apr 27, 2026
c01f85f
Address codex comments
yasen5 Apr 27, 2026
389daf3
Tested
yasen5 Apr 28, 2026
dff89fb
Fix first again
yasen5 Apr 28, 2026
e60f9d2
Merge branch 'unambiguous-refactor' into joint-solve
yasen5 Apr 28, 2026
f0a38a2
Last cleanup fs
yasen5 Apr 28, 2026
7243071
Actual actual last cleanup
yasen5 Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions constants/camera_constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@
"pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0",
"intrinsics_path": "/bos/constants/misc/dev_orin_intrinsics.json",
"extrinsics_path": "/bos/constants/misc/dev_orin_extrinsics.json",
"name": "dev_orin"
"name": "dev_orin",
"backlight": null,
"frame_width": 1280,
"frame_height": 800,
"fps": 100.0,
"exposure": null,
"port": 5801,
"detector_type": "austin_gpu",
"serial_id": "devorin",
"streamer_fps": 1
},
{
"name": "main_bot_front",
Expand Down Expand Up @@ -101,4 +110,4 @@
"detector_type": "austin_gpu"
}
]
}
}
19 changes: 9 additions & 10 deletions constants/misc/dummy_camera_intrinsics.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"cx": 10.0,
"cy": 10.0,
"fx": 1.0,
"fy": 1.0,
"k1": 0.0,
"k2": 0.0,
"k3": 0.0,
"p1": 0.0,
"p2": 0.0
"cx": 669.6571418509437,
"cy": 386.5080452595225,
"fx": 898.443795092944,
"fy": 898.4888081021497,
"k1": 0,
"k2": 0,
"k3": 0,
"p1": 0,
"p2": 0
}

8 changes: 8 additions & 0 deletions constants/stovetop_bot/front_right_extrinsics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"translation_x": -0.313,
"translation_y": 0.28,
"translation_z": 0.182,
"rotation_x": 0,
"rotation_y": -0.1717,
"rotation_z": 3.55
}
12 changes: 12 additions & 0 deletions constants/stovetop_bot/front_right_intrinsics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"cx": 1012.0165186318737,
"cy": 509.96268009601044,
"fx": 598.8562147696354,
"fy": 598.7791606886024,
"k1": 0.01265280096846185,
"k2": -0.02749231422823394,
"k3": 0.004217234226796008,
"p1": 6.344713648157099e-05,
"p2": -0.0015890075748726767
}

Binary file added position_log.wpilog
Binary file not shown.
2 changes: 2 additions & 0 deletions src/camera/camera_constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ auto GetCameraConstants(const std::string& path) -> camera_constants_t {
SetConstant<double>("stream_ratio", camera_constant.stream_ratio,
camera_config);
SetConstant<uint>("port", camera_constant.port, camera_config);
SetConstant<uint>("streamer_fps", camera_constant.streamer_fps,
camera_config);

if (camera_config.contains("detector_type") &&
!camera_config["detector_type"].is_null()) {
Expand Down
5 changes: 5 additions & 0 deletions src/camera/camera_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ using camera_constant_t = struct CameraConstant {
std::optional<std::string> serial_id = std::nullopt; // uvc only
std::optional<double> stream_ratio = std::nullopt;
std::optional<uint> port = std::nullopt;
std::optional<uint> streamer_fps = std::nullopt;
DetectorType detector_type = INVALID;

auto operator==(const CameraConstant& other) const -> bool {
return name == other.name;
}

friend auto operator<<(std::ostream& os, const CameraConstant& c)
-> std::ostream& {
os << "pipeline: " << c.pipeline.value_or("NO PIPELINE VALUE")
Expand Down
4 changes: 2 additions & 2 deletions src/camera/cv_camera.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ CVCamera::CVCamera(const CameraConstant& c, std::optional<std::string> log_path)
auto CVCamera::GetFrame() -> timestamped_frame_t {
timestamped_frame_t timestamped_frame;
cv::Mat raw_image;
if (!cap_.grab()) {
while (!cap_.grab()) {
Restart();
LOG(WARNING) << "Restarting camera";
}
Expand All @@ -76,7 +76,7 @@ auto CVCamera::GetFrame() -> timestamped_frame_t {
raw_image.copyTo(timestamped_frame.frame);

if (timestamped_frame.frame.empty()) {
timestamped_frame.frame = backup_image_;
timestamped_frame.invalid = true;
}
if (timestamped_frame.frame.channels() == 4) {
cv::cvtColor(timestamped_frame.frame, timestamped_frame.frame,
Expand Down
13 changes: 6 additions & 7 deletions src/camera/disk_camera.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ DiskCamera::DiskCamera(std::string image_folder_path,

double timestamp = std::stod(entry_name.erase(entry_name.size() - 4, 4));

if (start.has_value() && timestamp < start_) {
continue;
}
if (end.has_value() && timestamp > end_) {
continue;
}
// remove .png for timestamp
image_paths_.push(
timestamped_frame_path_t{.path = entry.path(), .timestamp = timestamp});
Expand All @@ -51,6 +45,12 @@ DiskCamera::DiskCamera(std::string image_folder_path,
}
image_paths_.pop();
entry.timestamp -= offset;
if (start.has_value() && entry.timestamp < start_) {
continue;
}
if (end.has_value() && entry.timestamp > end_) {
continue;
}
normalized.push(entry);
}
image_paths_ = std::move(normalized);
Expand All @@ -59,7 +59,6 @@ auto DiskCamera::GetFrame() -> timestamped_frame_t {
if (image_paths_.empty()) {
std::cout << "Finished reading all frames from DiskCamera. Folder path: "
<< image_folder_path_ << std::endl;
frc::DataLogManager::Stop();
return {.invalid = true};
}

Expand Down
3 changes: 2 additions & 1 deletion src/localization/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.10)

add_library(localization gpu_apriltag_detector.cc opencv_apriltag_detector.cc run_localization.cc nvidia_apriltag_detector.cc square_solver.cc joint_solver.cc multi_tag_solver.cc position_receiver.cc unambiguous_estimator.cc simulation_sender.cc networktable_sender.cc)
target_link_libraries(localization PUBLIC 971apriltag utils camera wpilibc wpiutil vpi absl::status)
target_sources(localization PRIVATE multi_camera_detector.cc)
target_link_libraries(localization PUBLIC ${OpenCV_LIBS} 971apriltag utils camera wpilibc wpiutil vpi absl::status)
77 changes: 77 additions & 0 deletions src/localization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Localization Module

We use AprilTags for localization.

---

## Base Interface

### `position_solver.h`
Interface for converting tag detections into robot pose estimates.

---

## Implementations

### `square_solver.{cc,h}`
Uses OpenCV's IPPE_SQUARE solver. Takes advantage of the fact that all object points are planar and a set distance and angle away from each other, so produces the best estimates for individual tag detections.

### `multi_tag_solver.{cc,h}`
Uses in multiple tags at a time, uses a different OpenCV algorithm because the points aren't all planar anymore once we pass in multiple tags. Can only be run on a per-camera basis because OpenCV limits what kinds of matrices you can pass into its functions, so we didn't figure out how to solve using all detections from all cameras at once using OpenCV.

### `joint_solver.{cc,h}`
Custom iterative solver using multiple tag estimates.

The normal PnP problem is:

```

image_point · scalar = image_to_camera · camera_to_world · world_relative_points;

```

In our scenario this is:

```

image_point · scalar = image_to_camera · camera_to_tag · tag_to_field · tag_center_to_tag_corner;

```

This can be rewritten as:

```

image_point · scalar = image_to_camera · camera_to_robot · robot_to_field · field_to_tag · tag_center_to_tag_corner;

```

And simplified to:

```

image_point · scalar = image_to_robot · robot_to_field · field_relative_tag_corner;

```

For an iterative solver, you would normally just compute the gradient here, but if you directly applied the gradient update to the rotation matrix, it wouldn't be guaranteed to be a rotation matrix. We therefore split up the transformation matrix robot_to_field into translation and individual Euler angles and update with respect to those variables instead.

After decomposition the math is effectively:

```

image_to_robot · translation · rotation_z · rotation_y · rotation_x · field_to_tag_center · tag_center_to_tag_corner = projected_corner_in_image

```

We treat like it is a neural network, with the layers being:

```

input/object_point → rotation_x → rotation_y → rotation_z → translation → image_to_robot = projection

```

If image_to_robot being at the end seems confusing, from_image_to_robot really means the robot's position expressed in image coordinates, meaning that multiplying image_to_robot by a matrix in robot coordinates takes it from robot coordinates to image coordinates (the projection).
```

Loading