Public warehouse-bot domain for DYNOS
Project description
dynos-warehouse-domain
A small, deliberately-toy domain and a bit of a tutorial. This represents a robot that moves between rooms, picks up boxes, and puts them down. It is the simplest interesting domain that exercises every part of the symbolic planning stack (preconditions, effects, typed objects, goals) without any of the AUV-specific machinery the Sentry domain layers on top.
If you have never used a task planner before, this is the package to read
first. This is sort of a sibling package to dynos-sentry-domain. They both
depend on dynos-client, but neither depends on the other; pick whichever fits
your problem. You probably want to learn the concepts here and then move to
dynos-sentry-domain.
Install
pip install dynos-warehouse-domain
Pulls in dynos-client and dynos-core transitively.
Symbolic planning in five minutes
DYNOS is a task planner. You hand it a description of the world and a goal; it hands you back a sequence of actions that, executed in order, will make the goal true. There are three pieces.
1. The world as a set of facts
DYNOS describes the world as a set of named, typed facts called fluents. A fluent is just a labeled question with a true/false answer:
robot_at(room_one)-> "is the robot in room one?"holding(box_a)-> "is the robot holding box A?"hands_free()-> "are the robot's hands empty?"
The set of fluents that are currently true is the world state. Anything not in that set is false. This is called a closed-world model, and it's why you only have to list what's true. It's also an approximation, but we work around it pretty effectively by constantly re-planning and re-assessing what goes on in the world.
World right now:
{ robot_at(room_robot_home), hands_free(), box_at(room_one, box_a) }
That's the entire description of the situation: robot is at home, hands are
empty, box A sits in room one. No coordinates, no images, no continuous sensor
data, just a set of true facts. Symbolic planning works on these boolean facts.
DYNOS does extend this by having a separate channel for continuous values like
coordinates and altitudes; that's ValueField on object types that will get
explored more later.
2. Actions described as before/after
A transition describes what an action looks like symbolically. It does not run any code. It declares:
- preconditions: facts that must already be true for the action to be legal
- adds: facts that become true after the action runs
- removes: facts that stop being true
The pick transition in this package, for example, says: "to pick up a box,
your hands must be free, you must be in the room with the box, and the box must
be in that room. Afterwards you're holding the box, the box is no longer in the
room, and your hands are no longer free."
We represent this in python, which aids with introspection and debug:
pick.define(
preconditions=[hands_free(),
robot_at(pick.params.room()),
box_at(pick.params.room(), pick.params.box())],
adds=[holding(pick.params.box())],
removes=[box_at(pick.params.room(), pick.params.box()), hands_free()],
)
The DYNOS backend reads the data when it plans, and there's a separate piece of
code (an @Action method, not in this package) that actually moves the robot.
The strict separation is what lets the planner reason about long sequences
without knowing anything about hardware.
3. Goals are desired states
A goal is a set of facts you want to be true. You don't tell the planner what to do; you tell it what should be true at the end. This way, the planner can figure out the optimization and missing steps for you, and can re-plan to re-solve when things change. For example:
Goal: { box_at(room_two, box_a) }
"I want box A to end up in room two." The planner searches over transitions to find a sequence whose combined effect changes the current state into one that satisfies the goal. For the world above, against this goal, the planner returns:
1. move(start=room_robot_home, end=room_one)
2. pick(room=room_one, box=box_a)
3. move(start=room_one, end=room_two)
4. place(room=room_two, box=box_a)
You did not write that sequence, the planner derived it from the preconditions
and effects you wrote. If the situation changes (e.g., an obstacle blocks
room_one) you describe the change as a fact the planner sees, set the same
goal, and you get a new plan. This is the dynos call goal "..." + dynos call execute workflow you'll see in the cross-package walkthrough, or that you can
trigger from the orchestrator (as the mission executive will do during
operation).
That's it. Everything else in DYNOS, like coverage surveys, lifecycle phases, multi-stage missions, and replanning on failure, is built on these three principles.
The warehouse domain concretely
This package ships everything you need to give the planner the example above.
Object types
class Box(ObjectType):
weight = ValueField(float)
class Room(ObjectType):
capacity = ValueField(int)
Box and Room are typed entities. Fluent argument types are checked against
them: box_at(room=Room, box=Box) will reject box_at(room_one, room_two)
because the second argument has the wrong type. The weight and capacity
ValueField descriptors hang continuous values off each instance — useful if
you want to later add a "robot can only carry boxes lighter than X kg"
constraint.
Pre-built objects
The package creates eight specific instances at import time, ready for goals:
- Boxes:
box_a,box_b,box_c - Rooms:
room_robot_home,room_loading_bay,room_one,room_two,room_three
This saves you from writing make_new_object(Box) for the toy examples; the
names are just stable handles into the numeric database.
Fluents
| Fluent | Meaning |
|---|---|
hands_free() |
True iff the robot is not currently holding anything. (Defaults to True, so the initial world has it true unless you explicitly remove it.) |
robot_at(room) |
The robot is in room. |
box_at(room, box) |
box is sitting in room. |
holding(box) |
The robot is holding box. |
Transitions (the robot's verbs)
| Transition | Parameters | Meaning |
|---|---|---|
pick(room, box) |
PickPlaceParams |
Pick up a box that's in the same room as the robot. |
place(room, box) |
PickPlaceParams |
Put down a held box in the room the robot is in. |
move(start, end) |
MoveParams |
Move from start to end. (No connectivity graph: every room is reachable from every other room. This is intentional for the toy.) |
Read warehouse.py for the full preconditions / adds / removes.
Use it
Against a running backend
Same flow as the Sentry domain (see dynos-client's README for the session/login basics). Once you have a session:
from dynos_client import RemoteOrchestrator
from dynos_warehouse import box_a, room_one, room_robot_home, room_two, box_at, robot_at, hands_free
orch = RemoteOrchestrator.from_config()
# Tell the backend the initial world
orch.set_start([
robot_at(room_robot_home),
hands_free(),
box_at(room_one, box_a),
])
# Set the goal
orch.set_goal([box_at(room_two, box_a)])
# Inspect the plan before running it
print(orch.get_plan())
# Run it
orch.execute_plan()
The CLI equivalent uses string-form goals:
dynos call goal "box_at(room_two, box_a)"
dynos call plan
dynos call execute
Offline (no backend at all)
Because Transition is inert data in dynos-core, you can introspect it without any backend or network:
from dynos_warehouse import pick
for p in pick.preconditions:
print(p)
# Output:
# hands_free()
# robot_at(room)
# box_at(room, box)
This is the fastest way to confirm you've understood a domain: print the preconditions and effects of every transition and check that the resulting before/after picture matches your mental model.
Public API
| Symbol | Kind | Purpose |
|---|---|---|
Box, Room |
object type | Typed entities. |
box_a, box_b, box_c, room_robot_home, room_loading_bay, room_one, room_two, room_three |
object | Pre-built instances. |
hands_free, robot_at, box_at, holding |
fluent | Boolean facts about the world. |
PickPlaceParams, MoveParams |
parameter dataclass | Transition argument types. |
pick, place, move |
transition | The robot's verbs. |
What's NOT here
- No executable behaviour. The transitions are symbolic specs; nothing in
this package moves a real or simulated robot. Pair it with a backend that has
registered
@Actionmethods bound topick/place/move. - No room connectivity graph.
move(start, end)is unconditional. If you want a topology where some rooms are only reachable from others, add aconnected(a, b)fluent and put it inmove's preconditions. - No payload constraints.
pickdoesn't checkBox.weightagainstRoom.capacity. Add a numeric precondition topick.define(...)if you want one — that's exactly the kind of extension this domain is meant for.
Next
dynos-core: the data-layer types (Transition,ObjectType,ValueField,make_new_fluent) you'll reach for when extending or replacing this domain.dynos-client: the HTTP client anddynosCLI for talking to a backend.dynos-sentry-domain: the same machinery applied to a real underwater vehicle, with surveys and lifecycle phases.user_guide.md: install + auth + first mission, end-to-end.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dynos_warehouse_domain-0.1.2.tar.gz.
File metadata
- Download URL: dynos_warehouse_domain-0.1.2.tar.gz
- Upload date:
- Size: 6.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e7d931b00254adee143c84a9f4620c7df934705126ff275eac0c61b65ceb326
|
|
| MD5 |
3c525d86d47558321d46f89a592d8b67
|
|
| BLAKE2b-256 |
dec2971a0c1d46828f824b0fc4b87bb3b494f7c5d24f4e03423e97236e9a7d9b
|
File details
Details for the file dynos_warehouse_domain-0.1.2-py3-none-any.whl.
File metadata
- Download URL: dynos_warehouse_domain-0.1.2-py3-none-any.whl
- Upload date:
- Size: 6.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e50f68efa513e13f6b19940dbc0e9f7cc6da33041bb8dae384337e00989362d1
|
|
| MD5 |
d54d542c3cd12ce66444d18d22a3dab5
|
|
| BLAKE2b-256 |
7a649a46fe0c71640241cf081c780aa6a22f9d6ae69482ea431ce204c350b64a
|