Skip to content

Commit 725b9e2

Browse files
committed
Add sheepdog_trials-specific code
- Get rid of the lookup tables 🎉 - Add marker information for sheepdog_trials And additionally...: - Add nix files so that I never have dependency issues again and if you do then you're jealous - Update the code minorly so that it'll work with fake-rpi (although without compromising *real* rpi support) Followup commit will: - Properly export all the enums
1 parent 3ee1741 commit 725b9e2

File tree

10 files changed

+300
-110
lines changed

10 files changed

+300
-110
lines changed

.envrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
if ! has nix_direnv_version || ! nix_direnv_version 2.1.1; then
2+
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.1.1/direnvrc" "sha256-b6qJ4r34rbE23yWjMqbmu3ia2z4b2wIlZUksBke/ol0="
3+
fi
4+
use flake

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,8 @@ $RECYCLE.BIN/
242242

243243
# End of https://www.gitignore.io/api/python,pycharm,windows
244244

245-
.vscode
245+
.vscode
246+
247+
# Direnv
248+
249+
.direnv/

flake.lock

Lines changed: 59 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
description = "A basic flake with a shell";
3+
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
4+
inputs.nixpkgs-minion.url = "github:Minion3665/nixpkgs";
5+
inputs.flake-utils.url = "github:numtide/flake-utils";
6+
7+
outputs = { self, nixpkgs, flake-utils, nixpkgs-minion }:
8+
flake-utils.lib.eachDefaultSystem (system:
9+
let
10+
pkgs = nixpkgs.legacyPackages.${system};
11+
pkgs-minion = nixpkgs-minion.legacyPackages.${system};
12+
in
13+
{
14+
devShells.default = pkgs.mkShell {
15+
nativeBuildInputs = [
16+
(pkgs-minion.python3.withPackages (pyPkgs: with pyPkgs;
17+
[
18+
smbus2
19+
opencv3
20+
fake-rpi
21+
scipy
22+
wiringpi
23+
]))
24+
];
25+
buildInputs = [ ];
26+
};
27+
});
28+
}

robot/__init__.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
"""The robot module, provides an python interface to the RoboCon hardware and
33
April tags a marker recognition system. Also performs convince functions for use
44
by shepherd"""
5+
6+
import importlib
7+
8+
has_picamera = importlib.find_loader("picamera") is not None
9+
10+
if not has_picamera:
11+
import sys
12+
import fake_rpi
13+
14+
sys.modules["RPi"] = fake_rpi.RPi
15+
sys.modules["RPi.GPIO"] = fake_rpi.RPi.GPIO
16+
sys.modules["picamera"] = fake_rpi.picamera
17+
sys.modules["smbus"] = fake_rpi.smbus
18+
519
import sys
620

721
# This log import configures our logging for us, but we don't want to
@@ -10,12 +24,33 @@
1024

1125
from robot.wrapper import Robot, NoCameraPresent
1226
from robot.greengiant import OUTPUT, INPUT, INPUT_ANALOG, INPUT_PULLUP
13-
from robot.vision import MARKER_ARENA, MARKER_CUBE_WINKIE, MARKER_CUBE_GILLIKIN, MARKER_CUBE_QUADLING, MARKER_CUBE_MUNCHKIN, MARKER_DEFAULT, RoboConUSBCamera
27+
from robot.vision import (
28+
MARKER_ARENA,
29+
MARKER_CUBE_WINKIE,
30+
MARKER_CUBE_GILLIKIN,
31+
MARKER_CUBE_QUADLING,
32+
MARKER_CUBE_MUNCHKIN,
33+
MARKER_DEFAULT,
34+
RoboConUSBCamera,
35+
)
1436

1537
MINIUM_VERSION = (3, 6)
1638
if sys.version_info <= MINIUM_VERSION:
17-
raise ImportError("Expected python {} but instead got {}".format(
18-
MINIUM_VERSION, sys.version_info))
39+
raise ImportError(
40+
"Expected python {} but instead got {}".format(MINIUM_VERSION, sys.version_info)
41+
)
1942

20-
__all__ = ["Robot", "NoCameraPresent", "OUTPUT", "INPUT", "INPUT_ANALOG",
21-
"INPUT_PULLUP", "MARKER_ARENA", "MARKER_CUBE_WINKIE", "MARKER_CUBE_GILLIKIN", "MARKER_CUBE_QUADLING", "MARKER_CUBE_MUNCHKIN", "MARKER_DEFAULT"]
43+
__all__ = [
44+
"Robot",
45+
"NoCameraPresent",
46+
"OUTPUT",
47+
"INPUT",
48+
"INPUT_ANALOG",
49+
"INPUT_PULLUP",
50+
"MARKER_ARENA",
51+
"MARKER_CUBE_WINKIE",
52+
"MARKER_CUBE_GILLIKIN",
53+
"MARKER_CUBE_QUADLING",
54+
"MARKER_CUBE_MUNCHKIN",
55+
"MARKER_DEFAULT",
56+
]

robot/apriltags3.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import numpy as np
2222
import scipy.spatial.transform as transform
2323

24+
from robot.sheepdog_trials.markers import MARKER
25+
2426

2527
######################################################################
2628
# Types
@@ -387,7 +389,7 @@ def __del__(self):
387389
self.libc.apriltag_detector_destroy.restype = None
388390
self.libc.apriltag_detector_destroy(self.tag_detector_ptr)
389391

390-
def detect(self, img, estimate_tag_pose=False, camera_params=None, tag_size_lut=None):
392+
def detect(self, img, estimate_tag_pose=False, camera_params=None):
391393
"""Run detectons on the provided image. The image must be a grayscale
392394
image of type np.uint8.
393395
# TODO get rid of the magic numbers
@@ -433,11 +435,7 @@ def detect(self, img, estimate_tag_pose=False, camera_params=None, tag_size_lut=
433435
if camera_params is None:
434436
raise ValueError(
435437
"camera_params must be provided to detect if estimate_tag_pose is set to True")
436-
if tag_size_lut is None:
437-
raise ValueError(
438-
"tag_size_lut must be provided to detect if estimate_tag_pose is set to True")
439-
440-
tag_size = tag_size_lut[tag.id]
438+
tag_size = MARKER.by_id(tag.id).size
441439

442440
camera_fx, camera_fy, camera_cx, camera_cy = [
443441
c for c in camera_params]

robot/sheepdog_trials/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from .teams import TEAM
2+
from .markers import (
3+
MARKER,
4+
MARKER_OWNER,
5+
MARKER_TYPE,
6+
ARENA_MARKER,
7+
SHEEP_MARKER,
8+
BASE_MARKER,
9+
)
10+
11+
__all__ = (
12+
"TEAM",
13+
"MARKER",
14+
"SHEEP_MARKER",
15+
"MARKER_TYPE",
16+
"MARKER_OWNER",
17+
"BASE_MARKER",
18+
"ARENA_MARKER",
19+
)

robot/sheepdog_trials/markers.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import enum
2+
3+
from .teams import TEAM
4+
5+
6+
class MARKER_TYPE(enum.Enum):
7+
SHEEP = enum.auto()
8+
ARENA = enum.auto()
9+
10+
11+
class MARKER_OWNER(enum.Enum):
12+
ME = enum.auto()
13+
ARENA = enum.auto()
14+
ANOTHER_TEAM = enum.auto()
15+
16+
17+
class WOOL_TYPE(enum.Enum):
18+
GOLDEN_FLEECE = enum.auto()
19+
STEEL_WOOL = enum.auto()
20+
21+
22+
class BASE_MARKER:
23+
def __init__(
24+
self,
25+
id: int,
26+
type: MARKER_TYPE,
27+
owner: MARKER_OWNER,
28+
) -> None:
29+
self.id = id
30+
31+
self.type = type
32+
self.owner = owner
33+
34+
self.owning_team: TEAM | None = None
35+
36+
self.wool_type: WOOL_TYPE | None = None
37+
38+
# Sizes are in meters
39+
self.size = 0.290 if self.type == MARKER_TYPE.ARENA else 0.08
40+
41+
def __repr__(self) -> str:
42+
return f"<Marker type={self.type} wool_type={self.wool_type} owner={self.owner} owning_team={self.owning_team} />"
43+
44+
@property
45+
def bounding_box_color(self) -> tuple[int, int, int]:
46+
return 255, 0, 0
47+
48+
49+
class ARENA_MARKER(BASE_MARKER):
50+
def __init__(self, id: int) -> None:
51+
super().__init__(id, MARKER_TYPE.ARENA, MARKER_OWNER.ARENA)
52+
53+
def __repr__(self) -> str:
54+
return f"<Marker(ARENA)/>"
55+
56+
57+
class SHEEP_MARKER(BASE_MARKER):
58+
def __init__(
59+
self, id: int, owner: TEAM, my_team: TEAM | None, wool_type: WOOL_TYPE
60+
) -> None:
61+
super().__init__(
62+
id,
63+
MARKER_TYPE.SHEEP,
64+
MARKER_OWNER.ANOTHER_TEAM if owner != my_team else MARKER_OWNER.ME,
65+
)
66+
self.owning_team = owner
67+
68+
self.wool_type = wool_type
69+
70+
def __repr__(self) -> str:
71+
return f"<Marker(SHEEP) wool_type={self.wool_type} owning_team={self.owning_team} />"
72+
73+
74+
class MARKER(BASE_MARKER):
75+
@staticmethod
76+
def by_id(id: int, team: TEAM | None = None) -> ARENA_MARKER | SHEEP_MARKER:
77+
"""
78+
Get a marker object from an id
79+
80+
Marker IDs are between 0 and 99
81+
Low marker IDs (0-39) belong to teams. X0-X9 of each range of 10 may be
82+
assigned to sheep.
83+
84+
For the markers which belong to sheep, low marker IDs (X0-X5) will be
85+
sheep with STEEL_WOOL. High marker IDs (X6-X9) will be sheep with
86+
GOLDEN_FLEECE.
87+
88+
Higher marker IDs (40+) will be part of the arena. These markers will be
89+
spaced as in 2021-2022's competition (6 markers on each side of the Arena,
90+
the first 50cm away from the wall and each subsequent marker 1m away from
91+
there)
92+
93+
If no team is provided, all team-owned markers will be assumed to belong to
94+
ANOTHER_TEAM
95+
"""
96+
97+
if id >= 40:
98+
return ARENA_MARKER(id)
99+
100+
owning_team = TEAM[f"T{id // 10}"]
101+
102+
wool_type = WOOL_TYPE.GOLDEN_FLEECE if id % 10 > 5 else WOOL_TYPE.STEEL_WOOL
103+
104+
return SHEEP_MARKER(id, owning_team, team, wool_type)

robot/sheepdog_trials/teams.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import enum
2+
3+
# spell-checker:words ZHORA,PRIS,BATEY
4+
5+
class TEAM(enum.Enum):
6+
# TODO: Think of better team names
7+
LEON = "N6MAC41717"
8+
ZHORA = "N6FAB61216"
9+
PRIS = "N6FAB21416"
10+
ROY = "N6MAA10816"
11+
12+
T0 = "N6MAC41717"
13+
T1 = "N6FAB61216"
14+
T2 = "N6FAB21416"
15+
T3 = "N6MAA10816"
16+

0 commit comments

Comments
 (0)