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

Knowledge #210

Open
wants to merge 47 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
8643733
[knowledge] First draft of knowledge source
Tigul Apr 15, 2024
7d16285
[knowledge] Added Knowledge engine
Tigul Apr 16, 2024
db8874e
[knowledge] Update architecture for knowledge engine
Tigul May 28, 2024
628cf93
[datastructures] Added decision tree implementation
Tigul May 31, 2024
ec1d66a
[knowledge] Added more knowledge sources
Tigul May 31, 2024
827b93c
[DecisionTree] Documentation
Tigul May 31, 2024
d9b5459
Merge branch 'dev' of github.com:cram2/pycram into knowledge
Tigul Jun 11, 2024
925c479
[aspects] Added aspects and conditions
Tigul Jun 13, 2024
791731a
[designator] Added knowledge pre conditions to pick up
Tigul Jun 13, 2024
f42c3c2
Merge branch 'new-robot-description' of github.com:Tigul/pycram-1 int…
Tigul Jul 9, 2024
9d5bcd2
[aspects] New Aspects structure
Tigul Jul 9, 2024
fbce04f
[KnowledgeEngine] Method to call source with highest priority
Tigul Jul 10, 2024
025f46a
[Action desig] Fixed Typing
Tigul Jul 10, 2024
eb6749a
[Knowledge engine] Aspects and resolution of Aspects
Tigul Jul 16, 2024
646d0d2
[facts knowledge] Reference inmplementation of reasoning
Tigul Jul 16, 2024
138ce54
Merge branch 'dev' of github.com:cram2/pycram into knowledge
Tigul Jul 16, 2024
c207645
[Knowledge engine] Variables in Aspects
Tigul Jul 19, 2024
08d47fb
[knowledge] Refactor of knowledge related files
Tigul Jul 19, 2024
e20cc27
[Aspects] Small fixes and better exception return
Tigul Aug 5, 2024
dba333c
[designator] Removed resolver parameter
Tigul Aug 5, 2024
016474a
[knowledge engine] Correction of facts knowledge
Tigul Aug 6, 2024
575d114
[aspects] New aspect structure
Tigul Aug 7, 2024
916c0ed
[Aspects] Renamed aspect to property
Tigul Sep 5, 2024
693f728
[knowledge/designator] Rename of property usage
Tigul Sep 5, 2024
6652053
[test] Fixed Test for pre-condition
Tigul Sep 5, 2024
9f38f72
[knowledge engine] Added flag to disable knowledge engine
Tigul Sep 5, 2024
a4891ac
[doc] Added documentation for knowledge interface
Tigul Sep 24, 2024
84c33f5
[knowledge] Removed input and output variables
Tigul Sep 25, 2024
f3db003
[Designator] Removed deprecated Designator class
Tigul Sep 25, 2024
57ca550
[property] Added reasoning result
Tigul Sep 25, 2024
8d00b0a
[Knowledge Engine] Added reasoned parameter matching
Tigul Sep 25, 2024
4df1c49
[action desigs] Added pre and post perform and small refactor
Tigul Sep 26, 2024
9a364e8
[datastructures] Added partial designator class
Tigul Oct 2, 2024
95fcacf
[partial desig] Fixed typing imports
Tigul Oct 9, 2024
88f6bf4
[plan failures] Fixed reasoning and collision errors
Tigul Oct 10, 2024
1948feb
[partial desig] Added filters for None arguments
Tigul Oct 11, 2024
41d98d0
[property] Added some helpful properties and doc
Tigul Oct 11, 2024
847a756
[knowledge engine] Added Reasoning instance and clean up
Tigul Oct 11, 2024
6448a1a
[Designator] Some small methods and doc
Tigul Oct 11, 2024
652aa38
[general] renaming
Tigul Oct 11, 2024
7c05875
[decision tree] Removed file
Tigul Oct 11, 2024
da5d3db
[action desig] Rename and first test of new desig resolution
Tigul Oct 11, 2024
c314a2b
[knowledge source] Fixed typing
Tigul Oct 11, 2024
12248b2
[doc] Added images and started doc about parameter reasoning
Tigul Oct 11, 2024
b30a8b3
Merge branch 'dev' of github.com:cram2/pycram into knowledge
Tigul Oct 17, 2024
61ec3fe
[examples] added knowledge source example
Tigul Nov 4, 2024
516afdb
[example] added property example
Tigul Nov 4, 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
Binary file added doc/images/knowledge/knowledge_arch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/knowledge/property_evaluation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/knowledge/property_resolution.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions doc/source/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ parts:
- file: new_robot.rst
- file: notebooks.rst
- file: designators.rst
- file: knowledge.rst

- caption: Trouble Shooting
chapters:
Expand Down Expand Up @@ -47,6 +48,9 @@ parts:
- file: notebooks/interface_examples/giskard.md
- file: notebooks/interface_examples/robokudo.md
- file: notebooks/ontology
- file: knowledge_examples.rst
sections:
- file: notebooks/knowledge_source.md

- caption: API
chapters:
Expand Down
87 changes: 87 additions & 0 deletions doc/source/knowledge.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
=========
Knowledge
=========

To be able to perform tasks in unknown environments a robot needs to have a way to reason and access
knowledge about the world. This knowledge can be represented in different ways and formats. In this
chapter we will discuss how PyCRAM access different kinds of knowledge and integrates them with
action designators.

-------
Concept
-------
The concept of knowledge in PyCRAM is based on the idea that knowledge can be represented in different ways and provided
from different sources which can be specialized for different tasks. These different sources of knowledge are implemented
behind a common interface which provides a set of methods to query the knowledge. This architecture can be seen in the
following image:

.. image:: ../images/knowledge/knowledge_arch.png
:align: center
:alt: Knowledge Architecture

The methods provided by the knowledge sources, are called "properties" since they are used to reason about the properties
of entities in the world. Properties can be combined to create more complex expressions which describe conditions
that have to be true at the time an action designator is executed. Let's look at an example explaining this:

.. code-block:: python
GraspableProperty(ObjectDesignator(...))
& ReachableProperty(Pose(....))

In this example, we have two properties, one that checks if an object is graspable and one that checks if a pose is reachable.
The `&` operator is used to combine the two properties into a single property that checks if both conditions are true at
the same time. This combined property stems from the PickUpAction where the object a robot wants to pick up has to be
reachable for the robot as well as being able to fit into the end effector of the robot.

Since knowledge sources are usually specialized for a certain task, they do not need to be able to implement all methods
of the interface. This leads to a lot of knowledge sources which all implement a subset of the methods, therefore no
knowledge source can be used to answer all questions. To solve this problem, PyCRAM has a central interface for processing
the combined properties and querying the knowledge sources called the "KnowledgeEngine". The job of the KnowledgeEngine
is to take a combined property and resolve the individual properties to the available knowledge sources which implement
the methods needed to answer the question. The resolved properties are then combined in the same way as the input property
and evaluated.

This image shows the process of resolving the properties through the knowledge engine:
.. image:: ../images/knowledge/property_resolution.png
:align: center
:alt: Property Resolution



-----------------
Knowledge Sources
-----------------
Knowledge does not have a unified form or representation, it can be available as an SQL database, a Knowledge Graph,
a simple JSON file, etc. To be able to handle a multitude of different representations of knowledge, PyCRAM uses the
concept of Knowledge Sources. A Knowledge Source is a class that implements a set of methods to access knowledge. Therefore,
PyCRAM does not care how the knowledge is accesses or where it is from as as long as the Knowledge Source implements the
abstract methods.

The methods that a Knowledge Source must implement are some basic methods to manage connecting to the knowledge itself
and more importantly, methods to query the knowledge. Which methods are provided by each knowledge source decides each
knowledge source on its own by using the respective property as a mix-in of the knowledge source. The properties curren
available and which a knowledge source can implement are:

- `GraspableProperty`: Checks if an object is graspable
- `ReachableProperty`: Checks if a pose is reachable
- `SpaceIsFreeProperty`: Checks if a space is free for the robot to move to
- `GripperIsFreeProperty`: Checks if the gripper is free to grasp an object
- `VisibleProperty`: Checks if an object is visible


## TODO link to example of knowledge source

----------------
Knowledge Engine
----------------
The Knowledge Engine is the central component in PyCRAM to reason about the world. It takes a combined property and
resolves the individual properties to the available knowledge sources which implement the methods needed to answer the
question.

While the properties are resolved they also infer parameter which are needed to execute the action but may not be defined
in the action designator description. For example, the PickUpAction needs an arm to pick up an object with, however, the
arm does not need to be defined in the action designator and can be inferred from the properties and the state of the
world.

After the properties are resolved, evaluated and the parameters are inferred, the Knowledge Engine grounds the action
in the belief state and tests if the found solution is valid and can achieve the goal. If the solution is valid, the
Knowledge Engine returns the solution and the action designator is performed.
24 changes: 24 additions & 0 deletions doc/source/knowledge_and_reasoning.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=================================
Knowledge and Reasoning in PyCRAM
=================================

The knowledge engine is able to infer parameters of a designator description from the context given by the properties
attached to its parameters. Since the properties are defined for the parameters of a designator description they add
semantic information to the designator description parameters. The knowledge engine is able to utilize this information
to infer the value of a parameter from the context.

Inference is done very similar to the normal reasoning process where the property function of the designator description
is first resolved and then evaluated. The difference is that we now not only look at the result (if the properties are
satisfied or not) but also a the possible parameter solutions that are generated while reasoning.

We start again by taking the properties of of the designator description and resolve them.

.. image:: ../images/knowledge/property_resolve.png
:alt: Source Resolve
:align: center

We then evaluate the properties and generate the possible parameter solutions.

.. image:: ../images/knowledge/property_evaluation.png
:alt: Source Evaluate
:align: center
3 changes: 3 additions & 0 deletions doc/source/knowledge_examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
=======================
Knowledge Examples
=======================
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the inclusion of the example missing here?

162 changes: 162 additions & 0 deletions examples/knowledge_source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
jupyter:
jupytext:
text_representation:
extension: .md
format_name: markdown
format_version: '1.3'
jupytext_version: 1.16.3
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
---

# How to create Knowledge Source
This notebook will detail what a knowledge source does, how it works and how you can create your own.

A knowledge source is part of the wider knowledge system in PyCRAM as explained [here](/knowledge). The purpose of a
knowledge source is to provide an interface to an external knowledge and reasoning system.

A knowledge source essentially consists of two parts, management methods which take care of connecting to the knowledge
system as well as registering the knowledge source with the knowledge engine and the implementation of the respective
reasoning queries which this knowledge source is able to process.

In this example we will walk through the process of creating a simple knowledge source and all steps involved in this process.

## Creating the Knowledge Source structure

We will start by creating the general structure of the Knowledge Source as well as the management methods. To do this
you have to create a new class which inherits from the ```KnowledgeSource``` class.

```python
from pycram.knowledge.knowledge_source import KnowledgeSource

class ExampleKnowledge(KnowledgeSource):

def __init__(self):
super().__init__(name="example", priority=0)
self.parameter = {}

def is_available(self) -> bool:
return True

def is_connected(self) -> bool:
return True

def connect(self):
pass

def clear_state(self):
self.parameter = {}
```

What we did in the code above was creating a class which inherits from the ```KowledgeSource``` base class, in the
constructor of this new class we initialize the base class with the name of the new Knowledge Source as well as a
priority. The priority is used to determine the order of all Knowledge Sources, in case two Knowledge Sources provide
the same reasoning queries the one with the higher priority is used.

Furthermore, we define a number of methods that manage the connection to the knowledge system namely the methods
```is_available```, ```is_connected``` and ```connect```. The first two methods just return a bool which states if the
knowledge system is available and connected to this Knowledge Source. The last method is used to create a connection
to the knowledge system. Since this is an example and we are not connecting to an external knowledge system the methods
are fairly trivial.

The last method we defined is ```clear_state```, this is used to clear any state the knowledge source itself might hold
either of the knowledge system or of this Knowledge Source class itself.


# Managing the resolvable Properties
Properties serve two purposes in the management of knowledge in PyCRAM, the first is to define semantic properties of
parameter of action designator. The second is to specify which properties or knowledge queries a Knowledge Source can
answer.

To define which properties a Knowledge Source can handle we simply use the respective properties as mix-in for the class
definition. With this let's take another look at our Knowledge Source with the handling of two properties.

```python
from pycram.knowledge.knowledge_source import KnowledgeSource
from pycram.datastructures.property import ReachableProperty, SpaceIsFreeProperty
from pycram.datastructures.world import World
from pycram.datastructures.pose import Pose
from pycram.datastructures.dataclasses import ReasoningResult
from pycram.costmaps import OccupancyCostmap
from pycram.ros.logging import loginfo
import numpy as np

class ExampleKnowledge(KnowledgeSource, ReachableProperty, SpaceIsFreeProperty):

def __init__(self):
super().__init__(name="example", priority=0)
self.parameter = {}

def is_available(self) -> bool:
return True

def is_connected(self) -> bool:
return True

def connect(self):
pass

def clear_state(self):
self.parameter = {}

def reachable(self, pose: Pose) -> ReasoningResult:
loginfo(f"Checking reachability for pose {pose}")
robot_pose = World.robot.pose
distance = pose.dist(robot_pose)
return ReasoningResult(distance < 0.6)

def space_is_free(self, pose: Pose) -> ReasoningResult:
loginfo(f"Checking if the space is free around {pose}")
om = OccupancyCostmap(0.2, False, 100, 0.02, pose)
return ReasoningResult(np.sum(om.map) == 6561)
```

Now we extend our Knowledge Source with the capability to handle two properties, Reachable and SpaceIsFree. As you can
see all we needed to do for this is to use the respective properties as mix-ins besides the Knowledge Source as well as
implement the method for each property which does the actual reasoning.

In this case the reasoning is kept fairly simple, since this is not the objective of this example. Reachable just
checks if a pose is within 60 centimeters of the robot while SpaceIsFree checks if a 2x2 meter square around the given
pose has no obstacles.

The methods doing the reasoning have to return a ```ReasoningResult``` instance, which contains a bool stating if the
reasoning succeeded or failed as well as additional parameters which might be inferred during reasoning. The additional
parameters are stated as key-value pairs in a dictionary.


# Testing the Knowledge Source
Since we now have a Knowledge Source which also implements two properties we can check if the Knowledge Source is used
to resolve the correct properties.

For this test we need a world as well as a robot.

```python
from pycram.worlds.bullet_world import BulletWorld
from pycram.world_concepts.world_object import Object
from pycram.datastructures.enums import WorldMode, ObjectType
from pycram.knowledge.knowledge_engine import KnowledgeEngine

world = BulletWorld(WorldMode.GUI)
pr2 = Object("pr2", ObjectType.ROBOT, "pr2.urdf")

target_pose = Pose([0.3, 0, 0.2])
property = ReachableProperty(target_pose) & SpaceIsFreeProperty(target_pose)

ke = KnowledgeEngine()
resolved_property = ke.resolve_properties(property)

print(f"Result of the property: {resolved_property()}")
```

As you can see we created a ```ReachableProperty``` as well as a ```SpaceIsFreeProperty``` and resolved them. For more
details on how properties and their resolution work please referee to the properties example.

Afterwards, we execute the properties, here we can see the logging infos from our Knowledge Source as well as the
confirmation that the implementation for both properties worked correctly.

```python
world.exit()
```
Loading
Loading