Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
tutorials:intermediate:json_prolog [2019/04/16 12:37] – fine tuning formatting hawkin | tutorials:intermediate:json_prolog [2022/01/17 13:00] – [Prerequisites] gkazhoya | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Using JSON Prolog to communicate with KnowRob | + | ====== Using JSON Prolog to communicate with KnowRob ====== |
- | The following tutorial will show how to query the knowledge | + | Tested under CRAM version v0.7.0 |
+ | |||
+ | The following tutorial will show how to query the knowledge | ||
==== Prerequisites ==== | ==== Prerequisites ==== | ||
- | We will assume that the previous tutorials have already been completed, and therefore | + | This tutorial requires |
Make sure you have **Java 8** and **Gradle 5.1** already installed. Otherwise you won't be able to build KnowRob. Other versions could work, but have not been tested yet, so use them at your own risk! | Make sure you have **Java 8** and **Gradle 5.1** already installed. Otherwise you won't be able to build KnowRob. Other versions could work, but have not been tested yet, so use them at your own risk! | ||
- | |||
=== Installation === | === Installation === | ||
Line 15: | Line 16: | ||
=== Installation troubleshooting === | === Installation troubleshooting === | ||
- | If it complains about missing | + | If it complains about missing |
<code bash> | <code bash> | ||
$ sudo apt-get install swi-prolog | $ sudo apt-get install swi-prolog | ||
</ | </ | ||
- | Make sure however, that it is **version 7**. There are major problems with locations of libraries with version 8, which is the version suggested by the swi-prolog website. apt-get should install version 7 by default. To make sure, you can check it with: | + | Make sure however, that it is **version 7**. There are major problems with locations of libraries with version 8, which is the version suggested by the swi-prolog website. apt-get should install version 7 by default |
<code bash> | <code bash> | ||
$ dpkg -s swi-prolog | $ dpkg -s swi-prolog | ||
Line 26: | Line 27: | ||
=== Launch === | === Launch === | ||
- | In addition to a **roscore** and the **roslisp_repl** you need to launch **json_prolog** in a Terminal with the following command: | + | In addition to a **roscore** and the **roslisp_repl** you need to launch **json_prolog** in a Terminal with the following command: |
+ | <code bash> | ||
+ | $ roslaunch rosprolog rosprolog | ||
+ | </ | ||
+ | or if you're using an older version of KnowRob, then the following command: | ||
<code bash> | <code bash> | ||
$ roslaunch json_prolog json_prolog.launch | $ roslaunch json_prolog json_prolog.launch | ||
Line 32: | Line 37: | ||
=== CRAM dependencies === | === CRAM dependencies === | ||
- | In order for us to be able to communicate with KnowRob from LISP, we need to load the **cram-json-prolog** | + | In order for us to be able to communicate with KnowRob from LISP, we need to load the '' |
In order to load the packages, do the following in your repl: | In order to load the packages, do the following in your repl: | ||
- | Open the Emacs command prompt with **,** and type **ros-load-system**. Hit **enter**. It will ask for you to enter the package you want to load, in which case you type **cram_json_prolog**. Confirm with enter. After a few seconds you should see **compilation finished** at the bottom line of Emacs. Repeat the process for **cram_utilities** as well. | + | <code lisp> |
+ | CL-USER> (ros-load:load-system | ||
+ | CL-USER> (ros-load: | ||
+ | </ | ||
If you want to add these dependencies directly into your package already, you can add them to your .asd file, as '' | If you want to add these dependencies directly into your package already, you can add them to your .asd file, as '' | ||
- | If you don't have a robot description currently published (and get thrown an error for that by Emacs), use the following instead: | + | To be able to communicated with KnowRob from CRAM over ROS, we need to start a ROS node in the REPL: |
<code lisp> | <code lisp> | ||
- | | + | |
</ | </ | ||
Line 59: | Line 67: | ||
</ | </ | ||
- | X being the variable we asked Prolog to fill with a value. Whenever Prolog returns a value for a variable, the variable name gets prefixed with a question mark. " | + | X being the variable we asked Prolog to fill with a value. Whenever Prolog returns a value for a variable, the variable name gets prefixed with a question mark. " |
- | In a [[json_prolog# | + | In a [[json_prolog# |
+ | |||
+ | Why do we get ''? | ||
+ | What does the second bracket do? | ||
+ | Let's add another variable to the query -- the variable ''? | ||
+ | <code lisp> | ||
+ | CL-USER> (json-prolog: | ||
+ | </ | ||
+ | This binds ''? | ||
+ | The result of the query is | ||
+ | <code lisp> | ||
+ | (((?X . 1) (?Y . 1))) | ||
+ | </ | ||
+ | Therefore, the second bracket makes a list of bindings for each variable in the query: | ||
+ | <code lisp> | ||
+ | ((?X . 1) (?Y . 1)) | ||
+ | </ | ||
+ | is a list of bindings for variable ''? | ||
+ | |||
+ | The last outer bracket is explained next. | ||
==== prolog-simple ==== | ==== prolog-simple ==== | ||
Line 72: | Line 99: | ||
</ | </ | ||
| | ||
- | The result is basically still the same, but it looks a little bit more complex on the first glance. What we get is a list which contains two elements. The first one is a list in a list with two elements which we already know from the previous call with '' | + | The result is basically still the same, but it looks a little bit more complex on the first glance. What we get is a list which contains two elements. The first one is a list in a list with two elements which we already know from the previous call with '' |
- | {1005F52FAB}> | + | {1005F52FAB}> |
==== lazy lists ==== | ==== lazy lists ==== | ||
The results we obtain from the queries are stored in lazy lists, meaning the information is loaded on demand, resulting in us seeing only one solution of the query. If we want to see all the results, we can expand the list and force LISP to show all of them to us, for example, by using: | The results we obtain from the queries are stored in lazy lists, meaning the information is loaded on demand, resulting in us seeing only one solution of the query. If we want to see all the results, we can expand the list and force LISP to show all of them to us, for example, by using: | ||
<code lisp> | <code lisp> | ||
- | CL-USER> (setq *our-list* (json-prolog: | + | |
+ | | ||
(((?X . 1)) | (((?X . 1)) | ||
. # | . # | ||
Line 90: | Line 117: | ||
We save our list in a variable for convenience purposes. This allows us also to expand the so stored list, and we can see all the possible values Prolog found for the variable ''? | We save our list in a variable for convenience purposes. This allows us also to expand the so stored list, and we can see all the possible values Prolog found for the variable ''? | ||
- | If we do the same with '' | + | Now we see why we need the outer bracket: the result of the query is a (lazy) list of all possible bindings for the query variables. Let us add another variable to the query again: |
<code lisp> | <code lisp> | ||
- | | + | CL-USER> (cut:force-ll (json-prolog: |
- | (((?X . 1))) | + | (((?X . 1) (?Y . 1)) ((?X . 2) (?Y . 2)) ((?X . 3) (?Y . 3))) |
- | CL-USER> | + | |
- | | + | |
</ | </ | ||
+ | We get a list of all possible bindings of the query variables, where the bindings is itself a list with one element for each variable. | ||
- | In the first case, meaning '' | + | If we evaluate our original '' |
+ | <code lisp> | ||
+ | CL-USER> (cut: | ||
+ | (((?X . 1)) ((?X . 2)) ((?X . 3))) | ||
+ | CL-USER> (cut: | ||
+ | (((?X . 1))) | ||
+ | </ | ||
+ | |||
+ | In the first case, meaning '' | ||
+ | |||
+ | One peculiarity of Prolog is that it can return an infinite number of bindings for a variable. | ||
+ | In that case, using '' | ||
+ | This is why lazy lists are useful when working with Prolog -- if the number of solutions is infinite or very big, using lazy lists one can work with one value at a time by iterating through the lazy list and getting new solutions on demand. | ||
| | ||
==== Accessing data from Prolog ==== | ==== Accessing data from Prolog ==== | ||
Now that we have a lazy list with interesting information, | Now that we have a lazy list with interesting information, | ||
| | ||
- | We can use '' | + | We can use '' |
<code lisp> | <code lisp> | ||
- | | + | CL-USER> (cut:lazy-car (json-prolog: |
- | | + | ((?X . 1)) |
- | | + | </ |
- | </ | + | Same goes for '' |
+ | <code lisp> | ||
+ | CL-USER> | ||
+ | (((?X . 2)) | ||
+ | . # | ||
+ | :GENERATOR #< | ||
+ | | ||
+ | </ | ||
- | This gives us the lazy list. Then we can use '' | + | When we have one element of the lazy list, we can use '' |
<code lisp> | <code lisp> | ||
- | | + | CL-USER> (cut: |
- | | + | 1 |
</ | </ | ||
- | Gives us the result '' | + | Gives us the result '' |
That way, one can extract the necessary information, | That way, one can extract the necessary information, | ||
Line 129: | Line 175: | ||
<code lisp> | <code lisp> | ||
- | |||
CL-USER> (json-prolog: | CL-USER> (json-prolog: | ||
- | (NIL | + | (NIL) |
- | . # | + | |
- | : | + | |
- | {1006444B3B}> | + | |
- | | + | |
</ | </ | ||
+ | |||
+ | In JSON Prolog '' | ||
| | ||
- | This allows us to use the **knowrob_common** package, which allows us to access many utility functions and also the most commonly used queries of KnowRob. | + | This allows us to use the '' |
With this imported package we could now do the following: | With this imported package we could now do the following: | ||
<code lisp> | <code lisp> | ||
- | + | | |
- | | + | (((?RESULT |
- | (((|?Result| | + | |
. # | . # | ||
:GENERATOR #< | :GENERATOR #< | ||
Line 151: | Line 193: | ||
</ | </ | ||
- | This query converts the first elements upper case letters into lower case, and stores the obtained result in the '' | + | This query converts the first elements upper case letters into lower case, and stores the obtained result in the '' |
- | An overview of the currently available packages and their queries can be found [[http:// | + | An overview of the currently available packages and their queries can be found [[http:// |
<code bash> | <code bash> | ||
| | ||
Line 162: | Line 204: | ||
| | ||
<code lisp> | <code lisp> | ||
- | |||
Prolog query failed: PrologException: | Prolog query failed: PrologException: | ||
[Condition of type SIMPLE-ERROR] | [Condition of type SIMPLE-ERROR] | ||
- | | ||
</ | </ | ||
| | ||
- | and if you check your terminal, in which the **json_prolog** node is running, there will be a huge java print as well, basically explaining the same thing. | + | and if you check your terminal, in which the '' |
| | ||
- | | + | |
- | ==== Some more information about Prolog in LISP ==== | + | ==== Case-sensitive variable names ==== |
+ | |||
+ | In the former query we got a weird result from Prolog which looked like this: | ||
+ | <code lisp> | ||
+ | |'' | ||
+ | </ | ||
+ | What do the pipes stand for in this case? | ||
+ | |||
+ | Lisp is case-insensitive, | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (eq 'hello ' | ||
+ | T | ||
+ | CL-USER> (eq 'hello ' | ||
+ | T | ||
+ | </ | ||
+ | |||
+ | Prolog, on the other hand, is case-sensitive. | ||
+ | And in some cases, one would like to have a case-sensitive variable in Lisp as well. | ||
+ | This is where the pipes come into play: | ||
+ | <code lisp> | ||
+ | CL-USER> (intern " | ||
+ | HELLO | ||
+ | :INTERNAL | ||
+ | CL-USER> (intern " | ||
+ | |Hello| | ||
+ | NIL | ||
+ | </ | ||
+ | The function '' | ||
+ | If the string is all capital, we get a normal usual symbol, in the other case we get the pipes. | ||
+ | |||
+ | The same is with Prolog, if your variable or any other symbol is not all caps, you will get a symbol in Lisp which has pipes around it: | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (json-prolog: | ||
+ | (((?X . 1))) | ||
+ | CL-USER> (json-prolog: | ||
+ | (((?MYVAR . 1))) | ||
+ | CL-USER> (json-prolog: | ||
+ | (((?MY_VAR . 1))) | ||
+ | CL-USER> (json-prolog: | ||
+ | (((|? | ||
+ | </ | ||
+ | |||
+ | In that case, to get the value of the variable you should also use pipes: | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (cut: | ||
+ | (car (json-prolog: | ||
+ | 1 | ||
+ | </ | ||
+ | |||
+ | Please note that in Prolog, all variables have to start with a capital letter and cannot contain dashes: | ||
+ | <code lisp> | ||
+ | CL-USER> (json-prolog: | ||
+ | NIL | ||
+ | CL-USER> (json-prolog: | ||
+ | NIL | ||
+ | </ | ||
+ | ==== (NIL) vs NIL ==== | ||
| | ||
If we want to check, if a certain number is member of the list, we must declare the variable accordingly. e.g. | If we want to check, if a certain number is member of the list, we must declare the variable accordingly. e.g. | ||
Line 202: | Line 301: | ||
CL-USER> (cut: | CL-USER> (cut: | ||
</ | </ | ||
- | returns a list of bindings for X and Y (Note: '' | + | returns a list of bindings for X and Y (//Note:// '' |
<code lisp> | <code lisp> | ||
(((?X . 1) (?Y . 3)) ((?X . 2) (?Y . 3))) | (((?X . 1) (?Y . 3)) ((?X . 2) (?Y . 3))) |