|
| 1 | +--- |
| 2 | +jupytext: |
| 3 | + text_representation: |
| 4 | + extension: .md |
| 5 | + format_name: myst |
| 6 | + format_version: 0.13 |
| 7 | + jupytext_version: 1.17.2 |
| 8 | +kernelspec: |
| 9 | + display_name: Python 3 |
| 10 | + language: python |
| 11 | + name: python3 |
| 12 | +--- |
| 13 | + |
| 14 | +(orm-guide)= |
| 15 | +# ORM Guide |
| 16 | + |
| 17 | +The semantic world comes with an ORM attached to it that is derived from the python datastructures. |
| 18 | +The ORM can be used to serialize entire worlds into an SQL database and retrieve them later. The semantic annotations (views) are stored alongside the kinematic information. |
| 19 | +The queried worlds are full objects that can be reconstructed into the original objects without any problems. |
| 20 | +Let's go into an example where we create a world, store it, retrieve and reconstruct it. |
| 21 | +First, lets load a world from a URDF file. |
| 22 | + |
| 23 | +```{code-cell} ipython2 |
| 24 | +import os |
| 25 | +
|
| 26 | +import rclpy |
| 27 | +from sqlalchemy import create_engine, select |
| 28 | +from sqlalchemy.orm import Session |
| 29 | +
|
| 30 | +from ormatic.dao import to_dao |
| 31 | +from semantic_world.adapters.urdf import URDFParser |
| 32 | +from semantic_world.orm.ormatic_interface import * |
| 33 | +from semantic_world.views import Table |
| 34 | +
|
| 35 | +# setup ros 2 |
| 36 | +rclpy.init() |
| 37 | +
|
| 38 | +# set up an in memory database |
| 39 | +engine = create_engine('sqlite:///:memory:') |
| 40 | +session = Session(engine) |
| 41 | +Base.metadata.create_all(bind=session.bind) |
| 42 | +
|
| 43 | +# load the table world from urdf |
| 44 | +urdf_dir = os.path.join(os.getcwd(), "..", "resources", "urdf") |
| 45 | +table = os.path.join(urdf_dir, "table.urdf") |
| 46 | +world = URDFParser(table).parse() |
| 47 | +``` |
| 48 | + |
| 49 | +Next, we create a semantic annotation that describes the table. |
| 50 | + |
| 51 | +```{code-cell} ipython2 |
| 52 | +table_view = Table([b for b in world.bodies if "top" in str(b.name)][0]) |
| 53 | +world.add_view(table_view) |
| 54 | +print(table_view) |
| 55 | +``` |
| 56 | + |
| 57 | +Now, let's store the world to a database. For that, we need to convert it to its data access object which than can be stored in the database. |
| 58 | + |
| 59 | +```{code-cell} ipython2 |
| 60 | +dao = to_dao(world) |
| 61 | +session.add(dao) |
| 62 | +session.commit() |
| 63 | +``` |
| 64 | + |
| 65 | +We can now query the database about the world and reconstruct it to the original instance. As you can see the semantic annotations are also available and fully working. |
| 66 | + |
| 67 | +```{code-cell} ipython2 |
| 68 | +queried_world = session.scalars(select(WorldMappingDAO)).one() |
| 69 | +reconstructed_world = queried_world.from_dao() |
| 70 | +table = [view for view in reconstructed_world.views if isinstance(view, Table)][0] |
| 71 | +print(table) |
| 72 | +print(table.points_on_table(2)) |
| 73 | +rclpy.shutdown() |
| 74 | +``` |
| 75 | + |
| 76 | +## Maintaining the ORM 🧰 |
| 77 | + |
| 78 | +You can maintain the ORM by maintaining the [generate_orm.py](https://github.com/cram2/semantic_world/blob/main/scripts/generate_orm.py). |
| 79 | +In there you have to list all the classes you want to generate mappings for and perhaps some type decorators for advanced use cases. |
| 80 | +Whenever you write a new dataclass that should appear or has semantic meaningful content make sure it appears in the set of classes. |
| 81 | +Pay attention to the logger during generation and see if it understands your datastructures correctly. |
| 82 | + |
| 83 | ++++ |
| 84 | + |
| 85 | +## The sharp bits 🔪 |
| 86 | +The world class manages the dependencies of the bodies in the world. Whenever you retrieve a body or connection, it comes as a data access object that is disconnected from the world itself. |
| 87 | +The relationships to the world exist and can be joined. However, when you reconstruct something else but the world, the reconstructed object does not have a world available. You can always reconstruct the entire world by querying for the objects world instead. |
0 commit comments