dynos-sentry-domain 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dynos_sentry_domain-0.1.2/PKG-INFO +169 -0
- dynos_sentry_domain-0.1.2/README.md +160 -0
- dynos_sentry_domain-0.1.2/pyproject.toml +15 -0
- dynos_sentry_domain-0.1.2/setup.cfg +4 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry/__init__.py +8 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry/_survey.py +86 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry/_vehicle.py +1 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry/sentry.py +22 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry_domain.egg-info/PKG-INFO +169 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry_domain.egg-info/SOURCES.txt +11 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry_domain.egg-info/dependency_links.txt +1 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry_domain.egg-info/requires.txt +1 -0
- dynos_sentry_domain-0.1.2/src/dynos_sentry_domain.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dynos-sentry-domain
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Public Sentry-vehicle survey domain for DYNOS
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: dynos-client>=0.1.2
|
|
9
|
+
|
|
10
|
+
# dynos-sentry-domain
|
|
11
|
+
|
|
12
|
+
The Sentry-AUV survey vocabulary for DYNOS. Mostly `Zone`, `Coordinate`, `survey`,
|
|
13
|
+
`goto`, `full_coverage_of`. Importing this package gives you the words to use
|
|
14
|
+
when constructing goals for a Sentry mission.
|
|
15
|
+
|
|
16
|
+
The package is backend-agnostic. It only declares the public domain. The
|
|
17
|
+
hardware extensions (thrusters, weights, descent sequencing, control modes)
|
|
18
|
+
live behind the backend boundary and are not shipped on PyPI; the planner knows
|
|
19
|
+
about them when it runs, but your code never references them.
|
|
20
|
+
|
|
21
|
+
Treat this repository like a static API for Sentry. `dynos-client` lets you
|
|
22
|
+
talk to a backend, `dynos-sentry-domain` gives you the words to use. You
|
|
23
|
+
usually install both together.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install dynos-sentry-domain
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Pulls in `dynos-client` (and `dynos-core`) transitively.
|
|
32
|
+
|
|
33
|
+
## Concepts
|
|
34
|
+
|
|
35
|
+
A short refresher of `dynos-core`:
|
|
36
|
+
|
|
37
|
+
- Symbolic state is a set of named facts (fluents) about typed objects.
|
|
38
|
+
`at(wp_end)` and `phase_at_depth()` are atoms; the current world is the set
|
|
39
|
+
of atoms that are true now.
|
|
40
|
+
- Goals are states not commands. You tell the system *what should be true*; the
|
|
41
|
+
planner derives the action sequence that makes it true.
|
|
42
|
+
- Transitions are action schemas. `survey(zone)` is the symbolic side; the
|
|
43
|
+
executable side is on the backend.
|
|
44
|
+
|
|
45
|
+
### `Zone`
|
|
46
|
+
|
|
47
|
+
A polygonal survey region. The planner expands `full_coverage_of(zone)` into
|
|
48
|
+
per-trackline `fly_trackline(...)` actions using the zone's geometry — you do
|
|
49
|
+
not enumerate the tracklines yourself.
|
|
50
|
+
|
|
51
|
+
| Field | Description | Example |
|
|
52
|
+
|--------------------|--------------------------------------------|-------------------------------------------|
|
|
53
|
+
| `vertices` | Polygon corners as `[lon, lat]` | `[[-70.67, 41.52], [-70.66, 41.52], ...]` |
|
|
54
|
+
| `coordinate_frame` | Currently only `"geographic"` is supported | `"geographic"` |
|
|
55
|
+
| `altitude` | Survey depth (m) | `65.0` |
|
|
56
|
+
| `speed` | Vehicle speed (m/s) | `1.0` |
|
|
57
|
+
| `coverage_width` | Sensor swath width (m) | `170.0` (multibeam), `5.0` (camera) |
|
|
58
|
+
| `robot_width` | Vehicle width (m) | `0.5` |
|
|
59
|
+
| `envelope` | Altitude tolerance (m) | `20.0` |
|
|
60
|
+
|
|
61
|
+
### `Coordinate`
|
|
62
|
+
|
|
63
|
+
A named waypoint. The planner uses it as a goto target.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
Coordinate(name="wp_end", latitude=41.525, longitude=-70.66, coordinate_frame="geographic")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Lifecycle phases
|
|
70
|
+
|
|
71
|
+
Sentry missions have lifecycle predicates that goals usually combine with the
|
|
72
|
+
survey or goto goal so the vehicle ends up in a known state when the plan
|
|
73
|
+
finishes:
|
|
74
|
+
|
|
75
|
+
- `phase_at_depth()`: vehicle is at operating depth.
|
|
76
|
+
- `phase_ascending()`: vehicle is ascending after the mission.
|
|
77
|
+
- `phase_surface()`: vehicle is at the surface.
|
|
78
|
+
|
|
79
|
+
These predicates are visible to the planner as goal terms. You typically
|
|
80
|
+
reference them as **strings** in `dynos call goal` or as bare term-builder
|
|
81
|
+
calls in Python (e.g. `phase_ascending()`); the backend resolves them
|
|
82
|
+
server-side.
|
|
83
|
+
|
|
84
|
+
## 60-second example: a single-zone survey
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from dynos_client import RemoteOrchestrator
|
|
88
|
+
from dynos_sentry.sentry import Zone, full_coverage_of
|
|
89
|
+
|
|
90
|
+
orch = RemoteOrchestrator.from_config(timeout_s=3600)
|
|
91
|
+
|
|
92
|
+
site_alpha = Zone("site_alpha",
|
|
93
|
+
coordinate_frame="geographic",
|
|
94
|
+
vertices=[[-70.67, 41.52], [-70.66, 41.52],
|
|
95
|
+
[-70.66, 41.525], [-70.67, 41.525]],
|
|
96
|
+
altitude=70.0,
|
|
97
|
+
envelope=20.0,
|
|
98
|
+
speed=0.8,
|
|
99
|
+
coverage_width=170.0,
|
|
100
|
+
robot_width=0.5,
|
|
101
|
+
)
|
|
102
|
+
orch.create_object(site_alpha)
|
|
103
|
+
orch.set_goal([full_coverage_of(site_alpha), "phase_ascending()"])
|
|
104
|
+
orch.execute_plan()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
CLI equivalent (using a `zone.json` file):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
dynos call create zone.json
|
|
111
|
+
dynos call goal "full_coverage_of(site_alpha)" "phase_ascending()"
|
|
112
|
+
dynos call plan # optional: print the action sequence
|
|
113
|
+
dynos call execute # synchronous; will time out the CLI for long surveys
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
After it finishes, pull a GeoJSON of the executed survey for inspection:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
dynos call geojson -o survey.geojson
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Goto: a goal, not a primitive
|
|
123
|
+
|
|
124
|
+
"Go to a waypoint" in DYNOS is a goal, not a command. You declare the world
|
|
125
|
+
state you want (like `at(wp_end) & phase_at_depth()`) and the planner derives
|
|
126
|
+
the prerequisite sequence (takeover, descent setup, the descent itself). There
|
|
127
|
+
is deliberately no "just go" button: routing through the planner is what keeps
|
|
128
|
+
the symbolic world consistent and what makes replanning on failure work.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from dynos_sentry.sentry import Coordinate
|
|
132
|
+
|
|
133
|
+
orch.create_object(Coordinate(name="wp_end", latitude=41.525, longitude=-70.66,
|
|
134
|
+
coordinate_frame="geographic"))
|
|
135
|
+
orch.set_goal(["at(wp_end)", "phase_at_depth()"])
|
|
136
|
+
orch.execute_plan()
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`dynos call plan` will show something like `takeover_control, setup_descent, descent_sequence(target=wp_end)`.
|
|
140
|
+
|
|
141
|
+
## Public API
|
|
142
|
+
|
|
143
|
+
| Symbol | Purpose |
|
|
144
|
+
|--------------------------|-----------------------------------------------------------------------------------|
|
|
145
|
+
| `Zone` | The polygonal survey region (object type with `ValueField` descriptors). |
|
|
146
|
+
| `Coordinate` | A named waypoint (object type). |
|
|
147
|
+
| `survey` | Transition for surveying a zone (used as a building block of `full_coverage_of`). |
|
|
148
|
+
| `goto` | Transition for moving between coordinates. |
|
|
149
|
+
| `full_coverage_of(zone)` | Goal-side fluent: "this zone has been fully surveyed." Use this in `set_goal`. |
|
|
150
|
+
| `SurveyParams` | Parameter dataclass for `survey`. |
|
|
151
|
+
| `CoordinateGotoParams` | Parameter dataclass for `goto`. |
|
|
152
|
+
|
|
153
|
+
Lifecycle phase predicates (`phase_at_depth`, `phase_ascending`,
|
|
154
|
+
`phase_surface`) are reachable via goal strings; the backend resolves them.
|
|
155
|
+
|
|
156
|
+
## What is intentionally not exposed
|
|
157
|
+
|
|
158
|
+
Hardware fluents (`Thruster`, `Servo`, `controller_survey`,
|
|
159
|
+
`bottom_follow_configured`, ...) and internal transitions (`takeover_control`,
|
|
160
|
+
`descent_sequence`, ...) live in the backend extension layer, not here. They
|
|
161
|
+
are stripped from the public release; user code must not depend on them. If you
|
|
162
|
+
find yourself wanting one, the right move is usually to phrase it as a goal
|
|
163
|
+
that the planner can satisfy, or to file an issue describing the use case.
|
|
164
|
+
|
|
165
|
+
## Next
|
|
166
|
+
|
|
167
|
+
For a complete worked example with a custom action and an end-to-end mission,
|
|
168
|
+
see `dynos-adaptive-resampling`. For the cross-package install / login /
|
|
169
|
+
first-survey walkthrough, see `user_guide.md`.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# dynos-sentry-domain
|
|
2
|
+
|
|
3
|
+
The Sentry-AUV survey vocabulary for DYNOS. Mostly `Zone`, `Coordinate`, `survey`,
|
|
4
|
+
`goto`, `full_coverage_of`. Importing this package gives you the words to use
|
|
5
|
+
when constructing goals for a Sentry mission.
|
|
6
|
+
|
|
7
|
+
The package is backend-agnostic. It only declares the public domain. The
|
|
8
|
+
hardware extensions (thrusters, weights, descent sequencing, control modes)
|
|
9
|
+
live behind the backend boundary and are not shipped on PyPI; the planner knows
|
|
10
|
+
about them when it runs, but your code never references them.
|
|
11
|
+
|
|
12
|
+
Treat this repository like a static API for Sentry. `dynos-client` lets you
|
|
13
|
+
talk to a backend, `dynos-sentry-domain` gives you the words to use. You
|
|
14
|
+
usually install both together.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install dynos-sentry-domain
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Pulls in `dynos-client` (and `dynos-core`) transitively.
|
|
23
|
+
|
|
24
|
+
## Concepts
|
|
25
|
+
|
|
26
|
+
A short refresher of `dynos-core`:
|
|
27
|
+
|
|
28
|
+
- Symbolic state is a set of named facts (fluents) about typed objects.
|
|
29
|
+
`at(wp_end)` and `phase_at_depth()` are atoms; the current world is the set
|
|
30
|
+
of atoms that are true now.
|
|
31
|
+
- Goals are states not commands. You tell the system *what should be true*; the
|
|
32
|
+
planner derives the action sequence that makes it true.
|
|
33
|
+
- Transitions are action schemas. `survey(zone)` is the symbolic side; the
|
|
34
|
+
executable side is on the backend.
|
|
35
|
+
|
|
36
|
+
### `Zone`
|
|
37
|
+
|
|
38
|
+
A polygonal survey region. The planner expands `full_coverage_of(zone)` into
|
|
39
|
+
per-trackline `fly_trackline(...)` actions using the zone's geometry — you do
|
|
40
|
+
not enumerate the tracklines yourself.
|
|
41
|
+
|
|
42
|
+
| Field | Description | Example |
|
|
43
|
+
|--------------------|--------------------------------------------|-------------------------------------------|
|
|
44
|
+
| `vertices` | Polygon corners as `[lon, lat]` | `[[-70.67, 41.52], [-70.66, 41.52], ...]` |
|
|
45
|
+
| `coordinate_frame` | Currently only `"geographic"` is supported | `"geographic"` |
|
|
46
|
+
| `altitude` | Survey depth (m) | `65.0` |
|
|
47
|
+
| `speed` | Vehicle speed (m/s) | `1.0` |
|
|
48
|
+
| `coverage_width` | Sensor swath width (m) | `170.0` (multibeam), `5.0` (camera) |
|
|
49
|
+
| `robot_width` | Vehicle width (m) | `0.5` |
|
|
50
|
+
| `envelope` | Altitude tolerance (m) | `20.0` |
|
|
51
|
+
|
|
52
|
+
### `Coordinate`
|
|
53
|
+
|
|
54
|
+
A named waypoint. The planner uses it as a goto target.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
Coordinate(name="wp_end", latitude=41.525, longitude=-70.66, coordinate_frame="geographic")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Lifecycle phases
|
|
61
|
+
|
|
62
|
+
Sentry missions have lifecycle predicates that goals usually combine with the
|
|
63
|
+
survey or goto goal so the vehicle ends up in a known state when the plan
|
|
64
|
+
finishes:
|
|
65
|
+
|
|
66
|
+
- `phase_at_depth()`: vehicle is at operating depth.
|
|
67
|
+
- `phase_ascending()`: vehicle is ascending after the mission.
|
|
68
|
+
- `phase_surface()`: vehicle is at the surface.
|
|
69
|
+
|
|
70
|
+
These predicates are visible to the planner as goal terms. You typically
|
|
71
|
+
reference them as **strings** in `dynos call goal` or as bare term-builder
|
|
72
|
+
calls in Python (e.g. `phase_ascending()`); the backend resolves them
|
|
73
|
+
server-side.
|
|
74
|
+
|
|
75
|
+
## 60-second example: a single-zone survey
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from dynos_client import RemoteOrchestrator
|
|
79
|
+
from dynos_sentry.sentry import Zone, full_coverage_of
|
|
80
|
+
|
|
81
|
+
orch = RemoteOrchestrator.from_config(timeout_s=3600)
|
|
82
|
+
|
|
83
|
+
site_alpha = Zone("site_alpha",
|
|
84
|
+
coordinate_frame="geographic",
|
|
85
|
+
vertices=[[-70.67, 41.52], [-70.66, 41.52],
|
|
86
|
+
[-70.66, 41.525], [-70.67, 41.525]],
|
|
87
|
+
altitude=70.0,
|
|
88
|
+
envelope=20.0,
|
|
89
|
+
speed=0.8,
|
|
90
|
+
coverage_width=170.0,
|
|
91
|
+
robot_width=0.5,
|
|
92
|
+
)
|
|
93
|
+
orch.create_object(site_alpha)
|
|
94
|
+
orch.set_goal([full_coverage_of(site_alpha), "phase_ascending()"])
|
|
95
|
+
orch.execute_plan()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
CLI equivalent (using a `zone.json` file):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
dynos call create zone.json
|
|
102
|
+
dynos call goal "full_coverage_of(site_alpha)" "phase_ascending()"
|
|
103
|
+
dynos call plan # optional: print the action sequence
|
|
104
|
+
dynos call execute # synchronous; will time out the CLI for long surveys
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
After it finishes, pull a GeoJSON of the executed survey for inspection:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
dynos call geojson -o survey.geojson
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Goto: a goal, not a primitive
|
|
114
|
+
|
|
115
|
+
"Go to a waypoint" in DYNOS is a goal, not a command. You declare the world
|
|
116
|
+
state you want (like `at(wp_end) & phase_at_depth()`) and the planner derives
|
|
117
|
+
the prerequisite sequence (takeover, descent setup, the descent itself). There
|
|
118
|
+
is deliberately no "just go" button: routing through the planner is what keeps
|
|
119
|
+
the symbolic world consistent and what makes replanning on failure work.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from dynos_sentry.sentry import Coordinate
|
|
123
|
+
|
|
124
|
+
orch.create_object(Coordinate(name="wp_end", latitude=41.525, longitude=-70.66,
|
|
125
|
+
coordinate_frame="geographic"))
|
|
126
|
+
orch.set_goal(["at(wp_end)", "phase_at_depth()"])
|
|
127
|
+
orch.execute_plan()
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`dynos call plan` will show something like `takeover_control, setup_descent, descent_sequence(target=wp_end)`.
|
|
131
|
+
|
|
132
|
+
## Public API
|
|
133
|
+
|
|
134
|
+
| Symbol | Purpose |
|
|
135
|
+
|--------------------------|-----------------------------------------------------------------------------------|
|
|
136
|
+
| `Zone` | The polygonal survey region (object type with `ValueField` descriptors). |
|
|
137
|
+
| `Coordinate` | A named waypoint (object type). |
|
|
138
|
+
| `survey` | Transition for surveying a zone (used as a building block of `full_coverage_of`). |
|
|
139
|
+
| `goto` | Transition for moving between coordinates. |
|
|
140
|
+
| `full_coverage_of(zone)` | Goal-side fluent: "this zone has been fully surveyed." Use this in `set_goal`. |
|
|
141
|
+
| `SurveyParams` | Parameter dataclass for `survey`. |
|
|
142
|
+
| `CoordinateGotoParams` | Parameter dataclass for `goto`. |
|
|
143
|
+
|
|
144
|
+
Lifecycle phase predicates (`phase_at_depth`, `phase_ascending`,
|
|
145
|
+
`phase_surface`) are reachable via goal strings; the backend resolves them.
|
|
146
|
+
|
|
147
|
+
## What is intentionally not exposed
|
|
148
|
+
|
|
149
|
+
Hardware fluents (`Thruster`, `Servo`, `controller_survey`,
|
|
150
|
+
`bottom_follow_configured`, ...) and internal transitions (`takeover_control`,
|
|
151
|
+
`descent_sequence`, ...) live in the backend extension layer, not here. They
|
|
152
|
+
are stripped from the public release; user code must not depend on them. If you
|
|
153
|
+
find yourself wanting one, the right move is usually to phrase it as a goal
|
|
154
|
+
that the planner can satisfy, or to file an issue describing the use case.
|
|
155
|
+
|
|
156
|
+
## Next
|
|
157
|
+
|
|
158
|
+
For a complete worked example with a custom action and an end-to-end mission,
|
|
159
|
+
see `dynos-adaptive-resampling`. For the cross-package install / login /
|
|
160
|
+
first-survey walkthrough, see `user_guide.md`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dynos-sentry-domain"
|
|
7
|
+
version = "0.1.2"
|
|
8
|
+
description = "Public Sentry-vehicle survey domain for DYNOS"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
dependencies = ["dynos-client>=0.1.2"]
|
|
13
|
+
|
|
14
|
+
[tool.setuptools.packages.find]
|
|
15
|
+
where = ["src"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Public-facing Sentry domain information. The backend expands
|
|
3
|
+
this with many more actions that aren't useful/safe for the scientist (like
|
|
4
|
+
being able to manipulate communication power channels) and other configurations
|
|
5
|
+
that don't change (like maximum depth).
|
|
6
|
+
|
|
7
|
+
You can treat this repository like a static Sentry API.
|
|
8
|
+
"""
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Public survey domain: types and transitions for survey zone planning.
|
|
2
|
+
|
|
3
|
+
This module contains the symbols that external collaborators need to
|
|
4
|
+
define survey zones, create waypoints, and navigate between them. It
|
|
5
|
+
has no references to vehicle hardware, mission lifecycle phases, or
|
|
6
|
+
internal survey expansion steps.
|
|
7
|
+
|
|
8
|
+
Collaborators should import from dynos_sentry.sentry (the facade),
|
|
9
|
+
not from this module directly.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dynos_client.domain import (
|
|
13
|
+
ObjectType,
|
|
14
|
+
Transition,
|
|
15
|
+
TransitionParams,
|
|
16
|
+
ValueField,
|
|
17
|
+
make_new_fluent,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
ObjectTypes: abstract things we can interact with in the world.
|
|
22
|
+
|
|
23
|
+
For this, that's a Zone (set of coordinates defining a space that we might want
|
|
24
|
+
to survey) and a Coordinate.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
class Zone(ObjectType):
|
|
28
|
+
"""A 2D survey polygon with coverage parameters."""
|
|
29
|
+
|
|
30
|
+
coordinate_frame = ValueField(str) # "geographic" | "local_enu"
|
|
31
|
+
altitude = ValueField(float) # survey altitude in meters
|
|
32
|
+
envelope = ValueField(float) # altitude envelope tolerance
|
|
33
|
+
speed = ValueField(float) # survey speed in m/s
|
|
34
|
+
coverage_width = ValueField(float) # swath width for coverage planning
|
|
35
|
+
robot_width = ValueField(float) # vehicle width for coverage planning
|
|
36
|
+
vertices = ValueField(list) # list of Coordinate or (lat, lon) tuples
|
|
37
|
+
angle = ValueField(float) # survey-line angle in degrees (0 = north)
|
|
38
|
+
pattern = ValueField(str) # route pattern: Boustrophedon, Snake, Spiral
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Coordinate(ObjectType):
|
|
42
|
+
"""A geographic or local-ENU waypoint."""
|
|
43
|
+
|
|
44
|
+
latitude = ValueField(float)
|
|
45
|
+
longitude = ValueField(float)
|
|
46
|
+
coordinate_frame = ValueField(str) # "geographic" | "local_enu"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
Fluents: things that can be true or false, forming part of a state and
|
|
51
|
+
represented in a first-order logic format.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Survey goal
|
|
55
|
+
full_coverage_of = make_new_fluent(zone=Zone)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
Transitions and Transition Parameters: Transitions are things the robot can
|
|
60
|
+
do to modify the environment; they have parameters that the backend can access
|
|
61
|
+
later (so we can talk about 'do a survey' but then also figure out the zones
|
|
62
|
+
that that relates to)
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
class SurveyParams(TransitionParams):
|
|
66
|
+
zone: Zone
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CoordinateGotoParams(TransitionParams):
|
|
70
|
+
"""Parameters for the goto transition: two waypoints defining the leg."""
|
|
71
|
+
|
|
72
|
+
start: Coordinate
|
|
73
|
+
end: Coordinate
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# survey: expand a zone into tracklines.
|
|
77
|
+
survey = Transition(
|
|
78
|
+
params_type=SurveyParams,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# goto: navigate between two coordinates. Used directly for point-to-point
|
|
82
|
+
# navigation, and internally by the survey script for transit legs between
|
|
83
|
+
# tracklines.
|
|
84
|
+
goto = Transition(
|
|
85
|
+
params_type=CoordinateGotoParams,
|
|
86
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Vehicle internals are not included in the public distribution.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sentry Domain (public subset).
|
|
3
|
+
"""
|
|
4
|
+
from dynos_sentry._survey import ( # noqa: F401
|
|
5
|
+
Coordinate,
|
|
6
|
+
CoordinateGotoParams,
|
|
7
|
+
SurveyParams,
|
|
8
|
+
Zone,
|
|
9
|
+
full_coverage_of,
|
|
10
|
+
goto,
|
|
11
|
+
survey,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Coordinate",
|
|
16
|
+
"CoordinateGotoParams",
|
|
17
|
+
"SurveyParams",
|
|
18
|
+
"Zone",
|
|
19
|
+
"full_coverage_of",
|
|
20
|
+
"goto",
|
|
21
|
+
"survey",
|
|
22
|
+
]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dynos-sentry-domain
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Public Sentry-vehicle survey domain for DYNOS
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: dynos-client>=0.1.2
|
|
9
|
+
|
|
10
|
+
# dynos-sentry-domain
|
|
11
|
+
|
|
12
|
+
The Sentry-AUV survey vocabulary for DYNOS. Mostly `Zone`, `Coordinate`, `survey`,
|
|
13
|
+
`goto`, `full_coverage_of`. Importing this package gives you the words to use
|
|
14
|
+
when constructing goals for a Sentry mission.
|
|
15
|
+
|
|
16
|
+
The package is backend-agnostic. It only declares the public domain. The
|
|
17
|
+
hardware extensions (thrusters, weights, descent sequencing, control modes)
|
|
18
|
+
live behind the backend boundary and are not shipped on PyPI; the planner knows
|
|
19
|
+
about them when it runs, but your code never references them.
|
|
20
|
+
|
|
21
|
+
Treat this repository like a static API for Sentry. `dynos-client` lets you
|
|
22
|
+
talk to a backend, `dynos-sentry-domain` gives you the words to use. You
|
|
23
|
+
usually install both together.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install dynos-sentry-domain
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Pulls in `dynos-client` (and `dynos-core`) transitively.
|
|
32
|
+
|
|
33
|
+
## Concepts
|
|
34
|
+
|
|
35
|
+
A short refresher of `dynos-core`:
|
|
36
|
+
|
|
37
|
+
- Symbolic state is a set of named facts (fluents) about typed objects.
|
|
38
|
+
`at(wp_end)` and `phase_at_depth()` are atoms; the current world is the set
|
|
39
|
+
of atoms that are true now.
|
|
40
|
+
- Goals are states not commands. You tell the system *what should be true*; the
|
|
41
|
+
planner derives the action sequence that makes it true.
|
|
42
|
+
- Transitions are action schemas. `survey(zone)` is the symbolic side; the
|
|
43
|
+
executable side is on the backend.
|
|
44
|
+
|
|
45
|
+
### `Zone`
|
|
46
|
+
|
|
47
|
+
A polygonal survey region. The planner expands `full_coverage_of(zone)` into
|
|
48
|
+
per-trackline `fly_trackline(...)` actions using the zone's geometry — you do
|
|
49
|
+
not enumerate the tracklines yourself.
|
|
50
|
+
|
|
51
|
+
| Field | Description | Example |
|
|
52
|
+
|--------------------|--------------------------------------------|-------------------------------------------|
|
|
53
|
+
| `vertices` | Polygon corners as `[lon, lat]` | `[[-70.67, 41.52], [-70.66, 41.52], ...]` |
|
|
54
|
+
| `coordinate_frame` | Currently only `"geographic"` is supported | `"geographic"` |
|
|
55
|
+
| `altitude` | Survey depth (m) | `65.0` |
|
|
56
|
+
| `speed` | Vehicle speed (m/s) | `1.0` |
|
|
57
|
+
| `coverage_width` | Sensor swath width (m) | `170.0` (multibeam), `5.0` (camera) |
|
|
58
|
+
| `robot_width` | Vehicle width (m) | `0.5` |
|
|
59
|
+
| `envelope` | Altitude tolerance (m) | `20.0` |
|
|
60
|
+
|
|
61
|
+
### `Coordinate`
|
|
62
|
+
|
|
63
|
+
A named waypoint. The planner uses it as a goto target.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
Coordinate(name="wp_end", latitude=41.525, longitude=-70.66, coordinate_frame="geographic")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Lifecycle phases
|
|
70
|
+
|
|
71
|
+
Sentry missions have lifecycle predicates that goals usually combine with the
|
|
72
|
+
survey or goto goal so the vehicle ends up in a known state when the plan
|
|
73
|
+
finishes:
|
|
74
|
+
|
|
75
|
+
- `phase_at_depth()`: vehicle is at operating depth.
|
|
76
|
+
- `phase_ascending()`: vehicle is ascending after the mission.
|
|
77
|
+
- `phase_surface()`: vehicle is at the surface.
|
|
78
|
+
|
|
79
|
+
These predicates are visible to the planner as goal terms. You typically
|
|
80
|
+
reference them as **strings** in `dynos call goal` or as bare term-builder
|
|
81
|
+
calls in Python (e.g. `phase_ascending()`); the backend resolves them
|
|
82
|
+
server-side.
|
|
83
|
+
|
|
84
|
+
## 60-second example: a single-zone survey
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from dynos_client import RemoteOrchestrator
|
|
88
|
+
from dynos_sentry.sentry import Zone, full_coverage_of
|
|
89
|
+
|
|
90
|
+
orch = RemoteOrchestrator.from_config(timeout_s=3600)
|
|
91
|
+
|
|
92
|
+
site_alpha = Zone("site_alpha",
|
|
93
|
+
coordinate_frame="geographic",
|
|
94
|
+
vertices=[[-70.67, 41.52], [-70.66, 41.52],
|
|
95
|
+
[-70.66, 41.525], [-70.67, 41.525]],
|
|
96
|
+
altitude=70.0,
|
|
97
|
+
envelope=20.0,
|
|
98
|
+
speed=0.8,
|
|
99
|
+
coverage_width=170.0,
|
|
100
|
+
robot_width=0.5,
|
|
101
|
+
)
|
|
102
|
+
orch.create_object(site_alpha)
|
|
103
|
+
orch.set_goal([full_coverage_of(site_alpha), "phase_ascending()"])
|
|
104
|
+
orch.execute_plan()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
CLI equivalent (using a `zone.json` file):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
dynos call create zone.json
|
|
111
|
+
dynos call goal "full_coverage_of(site_alpha)" "phase_ascending()"
|
|
112
|
+
dynos call plan # optional: print the action sequence
|
|
113
|
+
dynos call execute # synchronous; will time out the CLI for long surveys
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
After it finishes, pull a GeoJSON of the executed survey for inspection:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
dynos call geojson -o survey.geojson
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Goto: a goal, not a primitive
|
|
123
|
+
|
|
124
|
+
"Go to a waypoint" in DYNOS is a goal, not a command. You declare the world
|
|
125
|
+
state you want (like `at(wp_end) & phase_at_depth()`) and the planner derives
|
|
126
|
+
the prerequisite sequence (takeover, descent setup, the descent itself). There
|
|
127
|
+
is deliberately no "just go" button: routing through the planner is what keeps
|
|
128
|
+
the symbolic world consistent and what makes replanning on failure work.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from dynos_sentry.sentry import Coordinate
|
|
132
|
+
|
|
133
|
+
orch.create_object(Coordinate(name="wp_end", latitude=41.525, longitude=-70.66,
|
|
134
|
+
coordinate_frame="geographic"))
|
|
135
|
+
orch.set_goal(["at(wp_end)", "phase_at_depth()"])
|
|
136
|
+
orch.execute_plan()
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`dynos call plan` will show something like `takeover_control, setup_descent, descent_sequence(target=wp_end)`.
|
|
140
|
+
|
|
141
|
+
## Public API
|
|
142
|
+
|
|
143
|
+
| Symbol | Purpose |
|
|
144
|
+
|--------------------------|-----------------------------------------------------------------------------------|
|
|
145
|
+
| `Zone` | The polygonal survey region (object type with `ValueField` descriptors). |
|
|
146
|
+
| `Coordinate` | A named waypoint (object type). |
|
|
147
|
+
| `survey` | Transition for surveying a zone (used as a building block of `full_coverage_of`). |
|
|
148
|
+
| `goto` | Transition for moving between coordinates. |
|
|
149
|
+
| `full_coverage_of(zone)` | Goal-side fluent: "this zone has been fully surveyed." Use this in `set_goal`. |
|
|
150
|
+
| `SurveyParams` | Parameter dataclass for `survey`. |
|
|
151
|
+
| `CoordinateGotoParams` | Parameter dataclass for `goto`. |
|
|
152
|
+
|
|
153
|
+
Lifecycle phase predicates (`phase_at_depth`, `phase_ascending`,
|
|
154
|
+
`phase_surface`) are reachable via goal strings; the backend resolves them.
|
|
155
|
+
|
|
156
|
+
## What is intentionally not exposed
|
|
157
|
+
|
|
158
|
+
Hardware fluents (`Thruster`, `Servo`, `controller_survey`,
|
|
159
|
+
`bottom_follow_configured`, ...) and internal transitions (`takeover_control`,
|
|
160
|
+
`descent_sequence`, ...) live in the backend extension layer, not here. They
|
|
161
|
+
are stripped from the public release; user code must not depend on them. If you
|
|
162
|
+
find yourself wanting one, the right move is usually to phrase it as a goal
|
|
163
|
+
that the planner can satisfy, or to file an issue describing the use case.
|
|
164
|
+
|
|
165
|
+
## Next
|
|
166
|
+
|
|
167
|
+
For a complete worked example with a custom action and an end-to-end mission,
|
|
168
|
+
see `dynos-adaptive-resampling`. For the cross-package install / login /
|
|
169
|
+
first-survey walkthrough, see `user_guide.md`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/dynos_sentry/__init__.py
|
|
4
|
+
src/dynos_sentry/_survey.py
|
|
5
|
+
src/dynos_sentry/_vehicle.py
|
|
6
|
+
src/dynos_sentry/sentry.py
|
|
7
|
+
src/dynos_sentry_domain.egg-info/PKG-INFO
|
|
8
|
+
src/dynos_sentry_domain.egg-info/SOURCES.txt
|
|
9
|
+
src/dynos_sentry_domain.egg-info/dependency_links.txt
|
|
10
|
+
src/dynos_sentry_domain.egg-info/requires.txt
|
|
11
|
+
src/dynos_sentry_domain.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dynos-client>=0.1.2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dynos_sentry
|