Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated pycrap to include soma and dul. #251

Open
wants to merge 30 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1e354ae
[Ontology] First version of ontology parser
tomsch420 Nov 28, 2024
c7f850b
[Ontology] First version of ontology parser
tomsch420 Nov 28, 2024
2a1a713
[Ontology] Pre commit before different way of parsing
tomsch420 Nov 29, 2024
09eddc4
[Ontology] Upgraded parser to work with all things in an ontology.
tomsch420 Nov 29, 2024
5fc8b0c
[Ontology] Updated ontology doc
tomsch420 Nov 29, 2024
28c3978
[Ontology] Documented parser.py
tomsch420 Nov 29, 2024
a3c6eb5
[Ontology] Refactored parser.py
tomsch420 Dec 3, 2024
d337ed7
[Ontology] Started to work on parser for multiple ontologies.
tomsch420 Dec 3, 2024
b3bf307
Some fixed to Errors regarding DataTypes,
sorinar329 Dec 4, 2024
46c51eb
"-" will be replaced with ""
sorinar329 Dec 5, 2024
d861a23
Added more fixes for datatypes, skipping AnnotationProperties for now.
sorinar329 Dec 5, 2024
5613552
First commit of urdfParser
sorinar329 Dec 6, 2024
ea45165
[Ontology] Replaced pycrap with new ontologies
tomsch420 Dec 10, 2024
82c3c32
[Ontology] Intermediate commit before doing some name changes
tomsch420 Dec 11, 2024
3faaae4
[Ontology] Added explicit representation of owlready2 logical constructs
tomsch420 Dec 11, 2024
d5c957d
[Ontology] Added soma and dul
tomsch420 Dec 11, 2024
a06cc11
[Ontology] Fixed import for owlready2 name conflicts
tomsch420 Dec 11, 2024
52a8982
[Ontology] Fixed import for owlready2 name conflicts
tomsch420 Dec 11, 2024
ce9d8ea
[Ontology] Updated pycrap tests
tomsch420 Dec 12, 2024
a1ee159
[Ontology] Added crax for custom pycram classes
tomsch420 Dec 12, 2024
6ea426d
[Ontology] Fixed most imports
tomsch420 Dec 12, 2024
0384560
[Ontology] Fixed all tests
tomsch420 Dec 12, 2024
b180c56
[Ontology] Started to fix doc
tomsch420 Dec 12, 2024
5331007
[Ontology] Updated doc
tomsch420 Dec 12, 2024
bfef679
[Ontology] Updated doc
tomsch420 Dec 12, 2024
3cc6e82
Merge remote-tracking branch 'pycram/dev' into dev
tomsch420 Dec 12, 2024
5da6d6c
[Ontology] Merged with dev but did not update doc yet
tomsch420 Dec 12, 2024
b9e2dfa
[Ontology] Fixed doc
tomsch420 Dec 12, 2024
d0c3695
[Ontology] Updated requirements
tomsch420 Dec 12, 2024
a6485ea
[Ontology] Fixed demos
tomsch420 Dec 12, 2024
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
2 changes: 1 addition & 1 deletion demos/pycram_bullet_world_demo/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pycram.world_concepts.world_object import Object
from pycram.datastructures.dataclasses import Color
from pycram.ros_utils.viz_marker_publisher import VizMarkerPublisher
from pycrap import Robot, Apartment, Milk, Cereal, Spoon, Bowl
from pycrap.ontologies import Robot, Apartment, Milk, Cereal, Spoon, Bowl
import numpy as np


Expand Down
2 changes: 1 addition & 1 deletion demos/pycram_multiverse_demo/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pycram.robot_description import RobotDescription
from pycram.world_concepts.world_object import Object
from pycram.worlds.multiverse import Multiverse
from pycrap import PhysicalObject
from pycrap.ontologies import PhysicalObject


world = Multiverse()
Expand Down
2 changes: 1 addition & 1 deletion demos/pycram_multiverse_demo/fallschool_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pycram.worlds.bullet_world import BulletWorld
from pycram.worlds.multiverse import Multiverse
from pycram.ros_utils.viz_marker_publisher import VizMarkerPublisher
from pycrap import PhysicalObject
from pycrap.ontologies import PhysicalObject


@with_simulated_robot
Expand Down
8 changes: 4 additions & 4 deletions examples/action_designator.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import ObjectType, WorldMode
from pycram.datastructures.pose import Pose
import pycrap
from pycrap.ontologies import Robot, Milk, Apartment

world = BulletWorld(WorldMode.DIRECT)
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf", pose=Pose([1, 2, 0]))
apartmet = Object("apartment", pycrap.Apartment, "apartment.urdf")
milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([2.3, 2, 1.1]))
pr2 = Object("pr2", Robot, "pr2.urdf", pose=Pose([1, 2, 0]))
apartmet = Object("apartment", Apartment, "apartment.urdf")
milk = Object("milk", Milk, "milk.stl", pose=Pose([2.3, 2, 1.1]))
```

To move the robot we need to create a description and resolve it to an actual Designator. The description of navigation
Expand Down
8 changes: 4 additions & 4 deletions examples/bullet_world.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ First we need to import and create a BulletWorld.
from pycram.worlds.bullet_world import BulletWorld
from pycram.datastructures.pose import Pose
from pycram.datastructures.enums import ObjectType, WorldMode
import pycrap
from pycrap.ontologies import Milk, Cereal, Robot

world = BulletWorld(mode=WorldMode.DIRECT)
```
Expand All @@ -42,7 +42,7 @@ To spawn new things in the BulletWorld we need to import the Object class and cr
```python
from pycram.world_concepts.world_object import Object

milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([0, 0, 1]))
milk = Object("milk", Milk, "milk.stl", pose=Pose([0, 0, 1]))
```

<!-- #region -->
Expand Down Expand Up @@ -92,7 +92,7 @@ parameter. Since attachments are bi-directional it doesn't matter on which Objec
First we need another Object

```python
cereal = Object("cereal", pycrap.Cereal, "breakfast_cereal.stl", pose=Pose([1, 0, 1]))
cereal = Object("cereal", Cereal, "breakfast_cereal.stl", pose=Pose([1, 0, 1]))
```

```python
Expand Down Expand Up @@ -120,7 +120,7 @@ which contain every link or joint as key and a unique id, used by PyBullet, as v
We will see this at the example of the PR2:

```python
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
pr2 = Object("pr2", Robot, "pr2.urdf")
print(pr2.links)
```

Expand Down
13 changes: 6 additions & 7 deletions examples/cram_plan_tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ from pycram.world_concepts.world_object import Object
import anytree
import pycram.failures
import numpy as np
import pycrap
from pycrap.ontologies import Milk, Cereal, Robot, Kitchen, Spoon, Apartment, Bowl

np.random.seed(4)

Expand All @@ -49,9 +49,9 @@ else:
world = BulletWorld()
viz_marker_publisher = VizMarkerPublisher()

robot = Object("pr2", pycrap.Robot, "pr2.urdf")
robot = Object("pr2", Robot, "pr2.urdf")
robot_desig = ObjectDesignatorDescription(names=['pr2']).resolve()
apartment = Object("apartment", pycrap.Apartment, "apartment.urdf")
apartment = Object("apartment", Apartment, "apartment.urdf")
apartment_desig = ObjectDesignatorDescription(names=['apartment']).resolve()
table_top_name = "stove" if use_multiverse else "cooktop"
table_top = apartment.get_link_position(table_top_name)
Expand Down Expand Up @@ -89,7 +89,7 @@ def get_n_random_positions(pose_list, n=4, dist=0.5, random=True):

```python
from tf.transformations import quaternion_from_euler
import pycrap

from pycram.costmaps import SemanticCostmap
from pycram.pose_generator_and_validator import PoseGenerator

Expand All @@ -103,7 +103,7 @@ poses_list = list(PoseGenerator(edges_cm, number_of_samples=-1))
poses_list.sort(reverse=True, key=lambda x: np.linalg.norm(x.position_as_list()))
object_poses = get_n_random_positions(poses_list)
object_names = ["bowl", "breakfast_cereal", "spoon"]
object_types = [pycrap.Bowl, pycrap.Cereal, pycrap.Spoon]
object_types = [Bowl, Cereal, Spoon]
objects = {}
object_desig = {}
for obj_name, obj_type, obj_pose in zip(object_names, object_types, object_poses):
Expand Down Expand Up @@ -140,7 +140,6 @@ Finally, we create a plan where the robot parks his arms, walks to the kitchen c
execute the plan.

```python
import pycrap
from pycram.external_interfaces.ik import IKError
from pycram.datastructures.enums import Grasp

Expand All @@ -159,7 +158,7 @@ def plan(obj_desig: ObjectDesignatorDescription.Object, torso=0.2, place=counter
ParkArmsActionPerformable(Arms.BOTH).perform()
good_torsos.append(torso)
picked_up_arm = pose.reachable_arms[0]
grasp = Grasp.TOP if issubclass(obj_desig.world_object.obj_type, pycrap.Spoon) else Grasp.FRONT
grasp = Grasp.TOP if issubclass(obj_desig.world_object.obj_type, Spoon) else Grasp.FRONT
PickUpActionPerformable(object_designator=obj_desig, arm=pose.reachable_arms[0], grasp=grasp,
prepose_distance=0.03).perform()

Expand Down
4 changes: 2 additions & 2 deletions examples/improving_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ session = sqlalchemy.orm.sessionmaker(bind=engine)()
Now we construct an empty world with just a floating milk, where we can learn about PickUp actions.

```python
from pycrap import Robot, Milk
from pycrap.ontologies import Robot, Milk

world = BulletWorld(WorldMode.DIRECT)
print(world.prospection_world)
Expand Down Expand Up @@ -167,7 +167,7 @@ Next, we put the learned model to the test in a complex environment, where the m
area.

```python
from pycrap import Apartment
from pycrap.ontologies import Apartment
kitchen = Object("apartment", Apartment, "apartment.urdf")

milk.set_pose(Pose([0.5, 3.15, 1.04]))
Expand Down
12 changes: 6 additions & 6 deletions examples/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ It is possible to spawn objects and robots into the BulletWorld, these objects c
A BulletWorld can be created by simply creating an object of the BulletWorld class.

```python
import pycrap
from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import ObjectType, WorldMode
from pycram.datastructures.pose import Pose
from pycrap.ontologies import Milk, Cereal, Robot, Kitchen

world = BulletWorld(mode=WorldMode.DIRECT)

milk = Object("milk", pycrap.Milk, "milk.stl")
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
cereal = Object("cereal", pycrap.Cereal, "breakfast_cereal.stl", pose=Pose([1.4, 1, 0.95]))
milk = Object("milk", Milk, "milk.stl")
pr2 = Object("pr2", Robot, "pr2.urdf")
cereal = Object("cereal", Cereal, "breakfast_cereal.stl", pose=Pose([1.4, 1, 0.95]))
```

The BulletWorld allows to render images from arbitrary positions. In the following example we render images with the
Expand Down Expand Up @@ -91,7 +91,7 @@ Since everything inside the BulletWorld is an Object, even a complex environment
in the same way as the milk.

```python
kitchen = Object("kitchen", pycrap.Kitchen, "kitchen.urdf")
kitchen = Object("kitchen", Kitchen, "kitchen.urdf")
```

## Costmaps
Expand Down Expand Up @@ -294,7 +294,7 @@ Location Designators can create a position in cartesian space from a symbolic de
from pycram.designators.location_designator import *
from pycram.designators.object_designator import *

robot_desig = BelieveObject(types=[pycrap.Robot]).resolve()
robot_desig = BelieveObject(types=[Robot]).resolve()
milk_desig = BelieveObject(names=["milk"]).resolve()
location_desig = CostmapLocation(target=milk_desig, visible_for=robot_desig)

Expand Down
2 changes: 1 addition & 1 deletion examples/knowledge_source.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ from pycram.datastructures.enums import WorldMode, ObjectType
from pycram.knowledge.knowledge_engine import KnowledgeEngine
from pycram.datastructures.pose import Pose
from pycram.datastructures.property import ReachableProperty, SpaceIsFreeProperty
from pycrap import Robot
from pycrap.ontologies import Robot

world = BulletWorld(WorldMode.GUI)
pr2 = Object("pr2", Robot, "pr2.urdf")
Expand Down
4 changes: 2 additions & 2 deletions examples/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ plan.
If you are performing a plan with a simulated robot, you need a BulletWorld.

```python
import pycrap
from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import ObjectType
from pycrap.ontologies import Robot

world = BulletWorld()
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
pr2 = Object("pr2", Robot, "pr2.urdf")
```

```python
Expand Down
8 changes: 4 additions & 4 deletions examples/local_transformer.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ from pycram.world_concepts.world_object import Object
from pycram.datastructures.pose import Transform, Pose
from pycram.local_transformer import LocalTransformer
from pycram.datastructures.enums import WorldMode
import pycrap
```

## Initializing the World
Expand All @@ -55,10 +54,11 @@ These objects will be used in subsequent tasks, to provide the frames to which w
```python
from pycram.worlds.bullet_world import Object
from pycram.datastructures.enums import ObjectType
from pycrap.ontologies import Kitchen, Milk, Bowl

kitchen = Object("kitchen", pycrap.Kitchen, "kitchen.urdf")
milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([0.9, 1, 0.95]))
bowl = Object("bowl", pycrap.Bowl, "bowl.stl", pose=Pose([1.6, 1, 0.90]))
kitchen = Object("kitchen", Kitchen, "kitchen.urdf")
milk = Object("milk", Milk, "milk.stl", pose=Pose([0.9, 1, 0.95]))
bowl = Object("bowl", Bowl, "bowl.stl", pose=Pose([1.6, 1, 0.90]))
```

## Creating a Local Transfomer
Expand Down
10 changes: 5 additions & 5 deletions examples/location_designator.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import ObjectType, WorldMode
from pycram.datastructures.pose import Pose
import pycrap
from pycrap.ontologies import Apartment, Robot, Milk

use_multiverse = False
viz_marker_publisher = None
Expand All @@ -57,8 +57,8 @@ else:
world = BulletWorld()
viz_marker_publisher = VizMarkerPublisher()

apartment = Object("apartment", pycrap.Apartment, "apartment.urdf")
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
apartment = Object("apartment", Apartment, "apartment.urdf")
pr2 = Object("pr2", Robot, "pr2.urdf")
```

Next up we will create the location designator description, the {meth}`~pycram.designators.location_designator.CostmapLocation` that we will be using needs a
Expand Down Expand Up @@ -91,7 +91,7 @@ PR2 will be set to 0.2 since otherwise the arms of the robot will be too low to

```python
pr2.set_joint_position("torso_lift_joint", 0.2)
milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
milk = Object("milk", Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))

```

Expand Down Expand Up @@ -195,7 +195,7 @@ from pycram.designators.location_designator import *
apartment_desig = BelieveObject(names=["apartment"])
handle_name = "cabinet10_drawer1_handle" if use_multiverse else "handle_cab10_t"
handle_desig = ObjectPart(names=[handle_name], part_of=apartment_desig.resolve())
robot_desig = BelieveObject(types=[pycrap.Robot])
robot_desig = BelieveObject(types=[Robot])

access_location = AccessingLocation(handle_desig.resolve(), robot_desig.resolve(),
prepose_distance=0.03).resolve()
Expand Down
10 changes: 5 additions & 5 deletions examples/migrate_neems.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ from pycram.tasktree import with_tree
from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.designators.object_designator import *
import pycrap
from pycrap.ontologies import Robot, Kitchen, Milk, Cereal


class ExamplePlans:
def __init__(self):
self.world = BulletWorld("DIRECT")
self.pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
self.kitchen = Object("kitchen", pycrap.Kitchen, "kitchen.urdf")
self.milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
self.cereal = Object("cereal", pycrap.Cereal, "breakfast_cereal.stl", pose=Pose([1.3, 0.7, 0.95]))
self.pr2 = Object("pr2", Robot, "pr2.urdf")
self.kitchen = Object("kitchen", Kitchen, "kitchen.urdf")
self.milk = Object("milk", Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
self.cereal = Object("cereal", Cereal, "breakfast_cereal.stl", pose=Pose([1.3, 0.7, 0.95]))
self.milk_desig = ObjectDesignatorDescription(names=["milk"])
self.cereal_desig = ObjectDesignatorDescription(names=["cereal"])
self.robot_desig = ObjectDesignatorDescription(names=["pr2"]).resolve()
Expand Down
10 changes: 5 additions & 5 deletions examples/minimal_task_tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ from pycram.datastructures.pose import Pose
from pycram.datastructures.enums import ObjectType, WorldMode
import anytree
import pycram.failures
import pycrap
```

Next we will create a bullet world with a PR2 in a kitchen containing milk and cereal.

```python
from pycrap.ontologies import Milk, Cereal, Robot, Kitchen
world = BulletWorld(WorldMode.DIRECT)
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
kitchen = Object("kitchen", pycrap.Kitchen, "kitchen.urdf")
milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
cereal = Object("cereal", pycrap.Cereal, "breakfast_cereal.stl", pose=Pose([1.3, 0.7, 0.95]))
pr2 = Object("pr2", Robot, "pr2.urdf")
kitchen = Object("kitchen", Kitchen, "kitchen.urdf")
milk = Object("milk", Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
cereal = Object("cereal", Cereal, "breakfast_cereal.stl", pose=Pose([1.3, 0.7, 0.95]))
milk_desig = ObjectDesignatorDescription(names=["milk"])
cereal_desig = ObjectDesignatorDescription(names=["cereal"])
robot_desig = ObjectDesignatorDescription(names=["pr2"]).resolve()
Expand Down
5 changes: 2 additions & 3 deletions examples/motion_designator.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import ObjectType, WorldMode
from pycram.datastructures.pose import Pose
import pycrap
import pycrap.ontologies as pycrap

world = BulletWorld(WorldMode.DIRECT)
pr2 = Object("pr2", pycrap.Robot, "pr2.urdf")
Expand Down Expand Up @@ -116,14 +116,13 @@ from pycram.designators.motion_designator import DetectingMotion, LookingMotion
from pycram.process_module import simulated_robot
from pycram.datastructures.pose import Pose
from pycram.datastructures.enums import DetectionTechnique, DetectionState
from pycrap import Milk
from pycram.designators.object_designator import BelieveObject


with simulated_robot:
LookingMotion(target=Pose([1.5, 0, 1], [0, 0, 0, 1])).perform()

motion_description = DetectingMotion(technique=DetectionTechnique.TYPES,state=DetectionState.START, object_designator_description=BelieveObject(types=[Milk]),
motion_description = DetectingMotion(technique=DetectionTechnique.TYPES,state=DetectionState.START, object_designator_description=BelieveObject(types=[pycrap.Milk]),
region=None)

obj = motion_description.perform()
Expand Down
16 changes: 7 additions & 9 deletions examples/object_designator.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import ObjectType, WorldMode
from pycram.datastructures.pose import Pose
import pycrap
world = BulletWorld(WorldMode.DIRECT)
```

Expand All @@ -49,12 +48,11 @@ description which will be used to describe objects in the real world.
Since {meth}`~pycram.designators.object_designator.BelieveObject` describes Objects in the BulletWorld we create a few.

```python
import pycrap

kitchen = Object("kitchen", pycrap.Kitchen, "kitchen.urdf")
milk = Object("milk", pycrap.Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
cereal = Object("froot_loops", pycrap.Cereal, "breakfast_cereal.stl", pose=Pose([1.3, 0.9, 0.95]))
spoon = Object("spoon", pycrap.Spoon, "spoon.stl", pose=Pose([1.3, 1.1, 0.87]))
from pycrap.ontologies import Milk, Cereal, Kitchen, Spoon
kitchen = Object("kitchen", Kitchen, "kitchen.urdf")
milk = Object("milk", Milk, "milk.stl", pose=Pose([1.3, 1, 0.9]))
cereal = Object("froot_loops", Cereal, "breakfast_cereal.stl", pose=Pose([1.3, 0.9, 0.95]))
spoon = Object("spoon", Spoon, "spoon.stl", pose=Pose([1.3, 1.1, 0.87]))
```

Now that we have objects we can create an object designator to describe them. For the start we want an object designator
Expand All @@ -75,7 +73,7 @@ the world.
```python
from pycram.designators.object_designator import BelieveObject

object_description = BelieveObject(types=[pycrap.Milk, pycrap.Cereal])
object_description = BelieveObject(types=[Milk, Cereal])

print(object_description.resolve())
```
Expand Down Expand Up @@ -109,7 +107,7 @@ For this we need some objects, so if you didn't already spawn them you can use t
```python
from pycram.designators.object_designator import BelieveObject

object_description = BelieveObject(types=[pycrap.Milk, pycrap.Cereal])
object_description = BelieveObject(types=[Milk, Cereal])

for obj in object_description:
print(obj, "\n")
Expand Down
Loading
Loading