diff --git a/examples/overarching_tutorial/simulation/.docker/Dockerfile b/examples/overarching_tutorial/simulation/.docker/Dockerfile index 97f6522d..bab71c1f 100644 --- a/examples/overarching_tutorial/simulation/.docker/Dockerfile +++ b/examples/overarching_tutorial/simulation/.docker/Dockerfile @@ -11,7 +11,8 @@ RUN apt-get update && \ libxi-dev libxkbcommon-dev libxkbcommon-x11-dev libxrender-dev \ libnss3 libasound2t64 libxkbfile1 \ python3-pip python3-tk python3-wrapt python3-inflection \ - ros-jazzy-example-interfaces ros-jazzy-rqt-service-caller + ros-jazzy-example-interfaces ros-jazzy-rqt-service-caller \ + tmux # Create a ROS 2 workspace. RUN mkdir -p /convince_ws/src/ @@ -25,7 +26,14 @@ RUN pip3 install --break-system-packages -r requirements.txt # RUN source /opt/ros/jazzy/setup.bash && \ # rosdep install --from-paths src -y --ignore-src # (pyrobosim) -RUN git clone -b 4.3.3 https://github.com/sea-bass/pyrobosim.git src/pyrobosim + +RUN git clone -b 4.3.4 https://github.com/sea-bass/pyrobosim.git src/pyrobosim + +# Install REFINE-PLAN +RUN git clone -b overarching_demo https://github.com/convince-project/refine-plan.git src/refine-plan +RUN cd src/refine-plan; pip3 install --break-system-packages -r requirements.txt +RUN cd src/refine-plan; pip3 install --break-system-packages . +RUN rm -r src/refine-plan # # (smc_storm) # RUN curl -O -L https://github.com/convince-project/smc_storm/releases/download/0.0.6/smc_storm_executable.tar.gz && \ diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/LICENSE b/examples/overarching_tutorial/simulation/refine_plan_demo/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/README.md b/examples/overarching_tutorial/simulation/refine_plan_demo/README.md new file mode 100644 index 00000000..650d7086 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/README.md @@ -0,0 +1,124 @@ +# REFINE-PLAN Demo + +This demo shows the robot executing refined behaviours within the Pyrobosim simulation. + +## Building and Running the Docker Container for this Tutorial + +> [!WARNING] +> You must be in the `data-model/examples/overarching_tutorial/simulation` folder for this to work. + +### Building the Docker container + +```bash +docker build -t convince_tutorial -f .docker/Dockerfile . +``` + +### Running the Docker Container + +```bash +docker run -it --rm\ + --name convince_tutorial\ + -v /tmp/.X11-unix:/tmp/.X11-unix:rw\ + -v ${XAUTHORITY:-$HOME/.Xauthority}:/root/.Xauthority\ + -v ./tutorial_sim:/convince_ws/src/tutorial_sim\ + -v ./refine_plan_demo:/convince_ws/src/refine_plan_demo\ + -v ./.docker/build:/convince_ws/build\ + -e DISPLAY\ + -e QT_X11_NO_MITSHM=1\ + convince_tutorial\ + bash +``` + +## The Problem + +The robot must search for a loaf of bread as quickly as possible within the following house environment: + +![pyrobosim](images/pyrobosim.png) + +The bread appears stochastically at: +* The **dining table** with probability **0.5** +* The **fridge** with probability **0.1** +* The **kitchen table** with probability **0.1** +* The **side table** with probability **0.3** + +From this, it is clear that the bread is much more likely to appear in the dining room than the kitchen. + +## The Initial BT +We begin with an initial BT for this problem which does not consider uncertainty. +In this instance, the BT does not know the bread's location distribution and so searches for it by moving +**clockwise** around the environment until it finds it. +This is inefficient, as the robot searches the lower probability locations first. +A portion of the initial BT for the fridge can be seen below. The sequence node is repeated for the kitchen table, side table, and dining table. + +![initial_bt](images/refine_plan_tutorial_initial_bt.png) + +### Running the Initial BT +After starting the Docker container, run: + + +```bash +cd src/refine_plan_demo/bringup +./run_demo.sh initial +``` + +From this, a tmux session will begin with two windows. +The first window is for the simulation, and the second is for execution. +The execution window provides insights into the robot's decision-making. +Upon running the demo the robot will begin searching for the bread by moving clockwise through the house. + +## REFINE-PLAN +REFINE-PLAN is an automated tool for refining hand-designed BTs to improve robot performance under uncertainty. +We give an overview of the REFINE-PLAN framework in the image below: + +![framework](images/framework.png) + +REFINE-PLAN begins with an initial hand-designed BT and a simulator as input. +From the initial BT we extract a state space for planning using the condition nodes and blackboard. +Next, we learn two Bayesian networks per action node in simulation which capture the stochastic transition and cost dynamics of robot execution. +We then construct a Markov decision process using the learned Bayesian networks and extracted state space, and solve it using standard techniques to synthesise a policy. +This policy can then be converted back to a BT. + +### State Space Extraction +For this problem, we extract the following state factors: +* The robot's location in the house (hall, fridge, kitchen table, side table, dining table) +* A state factor for each location declaring whether the bread is present, or whether this information is unknown. + +### Data Collection for Model Learning +To learn the stochastic transition and cost dynamics of the environment we require a **data collection** phase in simulation. +Here the robot executes actions randomly in the simulator. The recorded transitions are then used to learn the Bayesian networks. + +To run data collection, you can run the following after starting the Docker container, where `` is a connection string to a Mongo database, e.g. `localhost:27017`: + +```bash +cd src/refine_plan_demo/bringup +./run_data_collection.sh +``` + +### Synthesising the Refined Behaviour +Given the recorded transitions from the data collection phase, we can then: +* Learn the Bayesian networks for each BT action node +* Construct the MDP +* Solve it using standard techniques + +These three steps can be executed for this problem by running the following [script](https://github.com/convince-project/refine-plan/blob/main/bin/overarching_demo_planning.py). +After running REFINE-PLAN, the robot learns the location distribution for the bread and prioritises searching higher probability locations first. +Visually, the robot now moves **anticlockwise** through the house, finding the bread quicker on average. + +### Running the Refined Behaviour +After starting the Docker container, run: + +```bash +cd src/refine_plan_demo/bringup +./run_demo.sh refined +``` + +From this, a tmux session will begin with two windows. +The first window is for the simulation, and the second is for execution. +The execution window provides insights into the robot's decision-making. +Upon running the demo the robot will begin searching for the bread by moving **anticlockwise** through the house. + +## Further Information +For more information on REFINE-PLAN, you can: +* Visit our official [documentation](https://convince-project.github.io/refine-plan/) +* Look at the [source code](https://github.com/convince-project/refine-plan) +* Read the [paper](https://ieeexplore.ieee.org/document/11246986) diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/run_data_collection.sh b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/run_data_collection.sh new file mode 100755 index 00000000..923769cc --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/run_data_collection.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Runs the simulation 100 times with proper clean up for data collection + +MONGO_CONNECTION_STRING=$1 + +for RUN in {0..99..1}; +do + echo "STARTING DATA COLLECTION RUN $(($RUN + 1))/100" + source ./start_sim.sh data $MONGO_CONNECTION_STRING + sleep 20s # Wait for everything to be properly setup before waiting + sh ./termination_checker.sh +done diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/run_demo.sh b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/run_demo.sh new file mode 100755 index 00000000..8df49582 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/run_demo.sh @@ -0,0 +1,5 @@ +#!/bin/bash +MODE=$1 + +# Runs the refined or initial policy in simulation +./start_sim.sh $MODE no_mongo \ No newline at end of file diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/start_sim.sh b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/start_sim.sh new file mode 100755 index 00000000..17ad214e --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/start_sim.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Starts up the refine plan demo + +SESSION=refine_plan_demo +POLICY_EXECUTOR_MODE=$1 +MONGO_CONNECTION_STRING=$2 + + +tmux -2 new-session -d -s $SESSION +tmux rename-window -t $SESSION 'pyrobosim' +tmux new-window -t $SESSION -n 'policy-executor' + +tmux select-window -t $SESSION:pyrobosim +tmux send-keys "ros2 run tutorial_sim run --ros-args -p detect_succ_prob:=1.0" C-m + +tmux select-window -t $SESSION:policy-executor +tmux send-keys "ros2 launch refine_plan_demo policy_executor.launch.py db_collection:=demo-$POLICY_EXECUTOR_MODE mode:=$POLICY_EXECUTOR_MODE db_connection_string:=$MONGO_CONNECTION_STRING" C-m \ No newline at end of file diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/termination_checker.sh b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/termination_checker.sh new file mode 100755 index 00000000..f303bacf --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/bringup/termination_checker.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Checks for termination and then ensures everything is killed properly + +NODE=$(ros2 node list | grep /policy_executor) +while [ ! -z "${NODE}" ] +do + echo "Policy Executor still running..." + sleep 5s + NODE=$(ros2 node list | grep /policy_executor) +done + +echo "Policy Executor Finished, Terminating." +# Kill absolutely everything and be extra sure +tmux kill-server +pkill -9 ros + +# Confirm that the node list is not empty +NODE_LIST=$(ros2 node list) +while [ ! -z "${NODE_LIST}" ] +do + echo "Node List not empty yet..." + sleep 5s + NODE_LIST=$(ros2 node list) +done +echo "Node List Empty, one last sleep to be safe." +sleep 10s + + diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/images/framework.png b/examples/overarching_tutorial/simulation/refine_plan_demo/images/framework.png new file mode 100644 index 00000000..8973cd4a Binary files /dev/null and b/examples/overarching_tutorial/simulation/refine_plan_demo/images/framework.png differ diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/images/pyrobosim.png b/examples/overarching_tutorial/simulation/refine_plan_demo/images/pyrobosim.png new file mode 100644 index 00000000..f7324d42 Binary files /dev/null and b/examples/overarching_tutorial/simulation/refine_plan_demo/images/pyrobosim.png differ diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/images/refine_plan_tutorial_initial_bt.png b/examples/overarching_tutorial/simulation/refine_plan_demo/images/refine_plan_tutorial_initial_bt.png new file mode 100644 index 00000000..9734717f Binary files /dev/null and b/examples/overarching_tutorial/simulation/refine_plan_demo/images/refine_plan_tutorial_initial_bt.png differ diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/launch/policy_executor.launch.py b/examples/overarching_tutorial/simulation/refine_plan_demo/launch/policy_executor.launch.py new file mode 100644 index 00000000..5268307a --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/launch/policy_executor.launch.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Launch file for the policy executor. + +Author: Charlie Street +Owner: Charlie Street +""" + +from ament_index_python.packages import get_package_share_directory +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node, SetParameter +from launch.actions import DeclareLaunchArgument +from launch import LaunchDescription +import os + + +def generate_launch_description(): + + # Set sim time + set_sim_time = SetParameter(name="use_sim_time", value=False) + + # All launch args + db_connection_string = LaunchConfiguration("db_connection_string") + db_name = LaunchConfiguration("db_name") + db_collection = LaunchConfiguration("db_collection") + mode = LaunchConfiguration("mode") + + db_connection_arg = DeclareLaunchArgument( + "db_connection_string", default_value="localhost:27017" + ) + db_name_arg = DeclareLaunchArgument("db_name", default_value="refine-plan-demo") + db_collection_arg = DeclareLaunchArgument("db_collection") + mode_arg = DeclareLaunchArgument("mode") + + policy_exec_node = Node( + package="refine_plan_demo", + executable="policy_executor", + name="policy_executor", + parameters=[ + { + "db_connection_string": db_connection_string, + "db_name": db_name, + "db_collection": db_collection, + "mode": mode, + } + ], + output="screen", + ) + + ld = LaunchDescription() + ld.add_action(set_sim_time) + ld.add_action(db_connection_arg) + ld.add_action(db_name_arg) + ld.add_action(db_collection_arg) + ld.add_action(mode_arg) + ld.add_action(policy_exec_node) + + return ld diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/package.xml b/examples/overarching_tutorial/simulation/refine_plan_demo/package.xml new file mode 100644 index 00000000..c472d1e0 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/package.xml @@ -0,0 +1,21 @@ + + + + refine_plan_demo + 0.0.0 + A REFINE-PLAN demo in Pyrobosim for the CONVINCE project. + charlie + Apache-2.0 + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + pyrobosim_msgs + refine-plan + pymongo + + + ament_python + + diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/params/refined_policy.yaml b/examples/overarching_tutorial/simulation/refine_plan_demo/params/refined_policy.yaml new file mode 100644 index 00000000..6c22c94b --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/params/refined_policy.yaml @@ -0,0 +1,2783 @@ +state_action_map: +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: fridge +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: kitchen_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table +- action: kitchen_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table +- action: kitchen_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: kitchen_table +- action: kitchen_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: kitchen_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: kitchen_table +- action: kitchen_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: kitchen_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: kitchen_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: kitchen_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: kitchen_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: kitchen_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table +- action: dining_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table +- action: side_tableTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: dining_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: fridge +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: side_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: fridge +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: side_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: fridge +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: side_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: fridge +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: side_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: dining_table +- action: side_tableTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table +- action: dining_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: hall +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: side_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: hall +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: side_table +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: hall +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: hall +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: hall +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: side_table +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: hall +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: side_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: hall +- action: kitchen_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: detect + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: dining_table +- action: kitchen_tableTOfridge + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: kitchen_tableTOfridge + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: None + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: fridge +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: detect + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: hallTOfridge + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: side_table +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: hall +- action: None + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: side_tableTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: None + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge +- action: side_tableTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: kitchen_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: kitchen_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: kitchen_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: side_tableTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge +- action: fridgeTOkitchen_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge +- action: kitchen_tableTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table +- action: None + bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: kitchen_table +- action: dining_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table +- action: dining_tableTOside_table + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge +- action: hallTOdining_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: hallTOfridge + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall +- action: kitchen_tableTOside_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOfridge + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: hall +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: kitchen_table +- action: hallTOfridge + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: hall +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall +- action: hallTOdining_table + bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall +- action: fridgeTOkitchen_table + bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: fridge +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table +- action: side_tableTOdining_table + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: dining_table +- action: None + bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: dining_table +state_factors: + bread_at_dining_table: + type: StateFactor + values: + - unknown + - 'no' + - 'yes' + bread_at_fridge: + type: StateFactor + values: + - unknown + - 'no' + - 'yes' + bread_at_kitchen_table: + type: StateFactor + values: + - unknown + - 'no' + - 'yes' + bread_at_side_table: + type: StateFactor + values: + - unknown + - 'no' + - 'yes' + location: + type: StateFactor + values: + - hall + - side_table + - dining_table + - kitchen_table + - fridge +state_value_map: +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall + value: 12.945596477445175 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table + value: 2.353046924499827 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 11.403685994206944 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table + value: 5.3777319600979645 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table + value: 2.9769975940675217 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table + value: 9.40368604034241 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 12.338910246089203 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: fridge + value: 1.999999999996933 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table + value: 3.709529212933494 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall + value: 14.569171084209444 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 4.032201837287866 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table + value: 10.210473318130834 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table + value: 10.338910315992294 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall + value: 12.860320157899004 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall + value: 9.020085654673066 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table + value: 5.444444444235679 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 10.999999999572864 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 8.999999987895773 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 10.999999998530958 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table + value: 8.99999999934045 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 7.444444378358694 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 6.063292480880652 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 2.659379090616186 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: 12.210473263360809 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table + value: 1.2104733182094884 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table + value: 11.225934964981356 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table + value: 2.9657535532120853 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table + value: 2.267770604989619 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table + value: 10.66820270111394 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: kitchen_table + value: 8.999999999624222 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: kitchen_table + value: 8.999999999624222 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: kitchen_table + value: 2.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: kitchen_table + value: 8.999999999624222 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: kitchen_table + value: 2.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 1.9999999999999996 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 12.66820273883263 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 10.668202460971102 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 1.9999999999999996 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 12.668202713686576 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table + value: 10.668202733243193 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 10.999999749193098 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 10.812441400339809 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 10.812441400339809 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 10.812441400339809 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 1.9999999991790645 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 10.372822749024317 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 1.9999999991790645 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 8.063292399276374 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 13.251928640890407 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 12.812441295921516 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: 13.22593482874016 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall + value: 13.471225620412245 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 1.8124414005292566 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 1.8124414005292566 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 1.8124414005292566 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 1.8124413787569074 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 1.3728227491420875 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 1.3728227326507536 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table + value: 0.9947642681122343 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table + value: 0.9947642681122343 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table + value: 2.8786760675038634 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table + value: 2.2259349652149694 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 4.0169723180278645 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027492924945 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table + value: 5.2442593125482 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027492924945 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027484398 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table + value: 8.9999999869321 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: fridge + value: 10.999999788638815 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: fridge + value: 10.999999788638815 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: fridge + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: fridge + value: 10.999999788638815 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: fridge + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: 23.260752171978783 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 0.6670088425440452 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 7.791735561899276 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 23.260751864109558 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall + value: 21.260752103797692 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table + value: 8.999999999624222 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall + value: 15.83680882441516 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 12.812441295921516 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 12.812441295921516 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 12.372822680204852 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 10.23106040742063 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: 10.592549552946119 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall + value: 14.486687267378368 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027492924945 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027419693628 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027492924945 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027419693628 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027484398 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: dining_table + value: 1.6682027419693628 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 1.8124414005292566 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table + value: 8.999999995670652 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table + value: 3.8941377145020293 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 8.231060524043906 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 2.6441495713320604 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: hall + value: 12.260752301841151 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table + value: 3.5760565647968456 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: hall + value: 12.260752301950959 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: hall + value: 9.020085630379114 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: hall + value: 12.260752301900428 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: hall + value: 12.260752301900428 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 1.9999999999785918 +- bread_at_dining_table: unknown + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: 10.592549552946446 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: 10.592549552946446 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 10.592549552946446 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 13.236699123742323 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'yes' + location: hall + value: 12.260752121494555 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: hall + value: 12.260752121494555 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: hall + value: 9.124726710581095 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: side_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: hall + value: 12.260752121494555 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 10.81244140017179 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table + value: 10.668202645455532 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: dining_table + value: 10.66820270111394 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: 1.9999999999999996 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 1.9999999999999996 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: fridge + value: 10.999999915547129 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: fridge + value: 1.9999999927185337 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 9.02008565425694 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: side_table + value: 8.999999995670652 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: fridge + value: 1.9999999927185337 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 12.812441286849173 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: 10.592549552946119 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall + value: 9.020085654732789 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: side_table + value: 8.9999999869321 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: hall + value: 9.020085654732789 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: 10.99999999956997 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: fridge + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 10.999999998529239 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'yes' + location: kitchen_table + value: 8.999999998707287 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'yes' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: side_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: side_table + value: 8.999999987895773 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 10.668202460971102 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: kitchen_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: dining_table + value: 10.668202645455532 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: 12.81244116901552 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: 12.81244116901552 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: fridge + value: .inf +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 12.372822553298853 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 12.372822553298853 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: fridge + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: fridge + value: 1.999999999996933 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge + value: 1.999999999996933 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge + value: 1.9999999927185337 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge + value: 1.9999999927185337 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: fridge + value: 0.0 +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: kitchen_table + value: 0.0 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: dining_table + value: 12.668202738765967 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: dining_table + value: 12.66820271364821 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: fridge + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: .inf +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: hall + value: 9.124726719351536 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table + value: 10.812441396403724 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall + value: 10.592549552944467 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: kitchen_table + value: 10.812441396403724 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: hall + value: 10.59254955293899 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table + value: 10.37282274570367 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall + value: 10.59254955293899 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: kitchen_table + value: 10.37282274570367 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: hall + value: 10.592549552944467 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: hall + value: 9.020085492905139 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: kitchen_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: hall + value: 9.124726658108154 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: 'no' + bread_at_side_table: 'no' + location: hall + value: 23.26075217119612 +- bread_at_dining_table: unknown + bread_at_fridge: 'yes' + bread_at_kitchen_table: unknown + bread_at_side_table: 'no' + location: hall + value: 23.260751863672464 +- bread_at_dining_table: 'no' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: 'no' + location: fridge + value: 1.999999999996933 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table + value: 1.8124414004859484 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: side_table + value: 1.8124414004859484 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'no' + bread_at_side_table: unknown + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table + value: 1.372822749109284 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: side_table + value: 1.372822749109284 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: unknown + bread_at_side_table: unknown + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: 'no' + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: dining_table + value: 0.0 +- bread_at_dining_table: 'yes' + bread_at_fridge: unknown + bread_at_kitchen_table: 'yes' + bread_at_side_table: unknown + location: dining_table + value: 0.0 diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/refine_plan_demo/__init__.py b/examples/overarching_tutorial/simulation/refine_plan_demo/refine_plan_demo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/refine_plan_demo/policy_executor.py b/examples/overarching_tutorial/simulation/refine_plan_demo/refine_plan_demo/policy_executor.py new file mode 100644 index 00000000..1a2acca2 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/refine_plan_demo/policy_executor.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python3 +"""A node for policy execution in CONVINCE overarching demo environment. + +This policy executor can run data collection or refined policy execution. + +Author: Charlie Street +Owner: Charlie Street +""" + +from refine_plan.models.policy import Policy +from refine_plan.models.state_factor import StateFactor +from pyrobosim_msgs.action import ExecuteTaskAction +from pyrobosim_msgs.msg import TaskAction +from refine_plan.models.state import State +from action_msgs.msg import GoalStatus +from rclpy.action import ActionClient +from pymongo import MongoClient +from datetime import datetime +from rclpy.node import Node +import random +import rclpy + +# Node to nodes we can directly reach from that node +GRAPH = {"hall": ["dining_table", "fridge"], + "dining_table": ["hall", "side_table"], + "side_table": ["dining_table", "kitchen_table"], + "kitchen_table": ["side_table", "fridge"], + "fridge": ["kitchen_table", "hall"]} + + +class PolicyExecutor(Node): + """A node for behaviour execution in the CONVINCE demo environment. + + Attributes: + _db_collection: The MongoDB collection for data logging (if mode == data) + _pyrobosim_client: The action client for Pyrobosim (navigation/detection) + _run_id: The ID for this run + _refined_policy: The refined policy, if used in this instance + _policy_fn: A function describing robot policy execution + _mode: The execution mode (data or refined) + _goal_fn: A function which takes a state history and returns True if goal reached + _bread_locs: A list of locations the bread could be at + _nav_locs: The list of locations the robot can move to + """ + + def __init__(self): + """Initialise the policy executor.""" + super().__init__("policy_executor") + self.declare_parameter("db_connection_string", rclpy.Parameter.Type.STRING) + self.declare_parameter("db_name", rclpy.Parameter.Type.STRING) + self.declare_parameter("db_collection", rclpy.Parameter.Type.STRING) + self.declare_parameter("mode", rclpy.Parameter.Type.STRING) + + self._run_id = random.getrandbits(32) + self._mode_setup() + self._db_setup() + self._setup_actions() + self._bread_locs = ["side_table", "dining_table", "kitchen_table", "fridge"] + self._nav_locs = ["hall"] + self._bread_locs + self.get_logger().info("Policy Executor Setup") + + def _at_bread(self, history): + """Returns True if the bread has been found and the robot is at that location. + + This is used for the refined policy. + + Args: + history: The state history + + Returns: + True if the bread has been found + """ + last_state = history[-1] + + for bread_loc in self._bread_locs: + if ( + last_state["bread_at_{}".format(bread_loc)] == "yes" + and last_state["location"] == bread_loc + ): + return True + + return False + + def _data_collect_goal(self, history): + """Returns True if 100 actions executed (101 states in history) or the bread is reached. + + This is used for data collection. + There's not much point collecting data once the bread is reached. + + Args: + history: The state history + + Returns: + True if 100 actions have been executed or the bread is reached + """ + + return len(history) > 100 or self._at_bread(history) + + def _mode_setup(self): + """Setup the policy and goal_fn for a given mode.""" + self._mode = self.get_parameter("mode").value + if self._mode == "data": + self._policy_fn = self._rand_action + self._goal_fn = self._data_collect_goal + elif self._mode == "refined": + self._refined_policy = Policy( + {}, policy_file="../params/refined_policy.yaml" + ) + self._policy_fn = self._refined_policy.get_action + self._goal_fn = self._at_bread + elif self._mode == "initial": + self._policy_fn = self._initial_bt + self._goal_fn = self._at_bread + + self.get_logger().info("Executing {} Policy".format(self._mode)) + + def _db_setup(self): + """Setup the Mongo connection.""" + if self._mode == "data": + connect_str = self.get_parameter("db_connection_string").value + db_name = self.get_parameter("db_name").value + db_collection = self.get_parameter("db_collection").value + self._db_collection = MongoClient(connect_str)[db_name][db_collection] + self.get_logger().info("Connected to MongoDB") + elif self._mode == "refined" or self._mode == "initial": + self._db_collection = None + self.get_logger().info(f"Running in {self._mode} mode - no Mongo logging") + + def _setup_actions(self): + """Setup the action client for all actions.""" + self._pyrobosim_client = ActionClient(self, ExecuteTaskAction, "execute_action") + self._pyrobosim_client.wait_for_server() + self.get_logger().info("Pyrobosim Action Execution Client Active") + + def _create_initial_state(self): + """Creates the initial state for policy execution. + + Returns: + The initial state + """ + loc_sf = StateFactor("location", self._nav_locs) + bread_sfs = [] + for bread_loc in self._bread_locs: + bread_sfs.append( + StateFactor("bread_at_{}".format(bread_loc), ["unknown", "no", "yes"]) + ) + + state_dict = {loc_sf: "hall"} + for sf in bread_sfs: + state_dict[sf] = "unknown" + + return State(state_dict) + + def _enabled_actions(self, state): + """Return the enabled actions in a state. + + Args: + state: The current state + + Returns: + A list of enabled actions + """ + enabled_actions = set([]) + + loc = state["location"] + if loc in self._bread_locs and state["bread_at_{}".format(loc)] == "unknown": + enabled_actions.add("detect") + + # Connect to locations in GRAPH + actions = [f"{state['location']}TO{next_loc}" for next_loc in GRAPH[state["location"]]] + enabled_actions.update(actions) + + return list(enabled_actions) + + def _rand_action(self, state): + """Select a random enabled action for data collection. + + Args: + state: Unused + + Returns: + The action to be executed + """ + return random.choice(self._enabled_actions(state)) + + def _initial_bt(self, state): + """Run the initial policy, which moves clockwise through the environment. + + Args: + state: The current state of the system + + Returns: + The next action to execute + """ + if state["location"] == "hall": + return "hallTOfridge" + elif state["location"] == "dining_table": + if state["bread_at_dining_table"] == "unknown": + return "detect" + elif state["location"] == "side_table": + if state["bread_at_side_table"] == "unknown": + return "detect" + return "side_tableTOdining_table" + elif state["location"] == "kitchen_table": + if state["bread_at_kitchen_table"] == "unknown": + return "detect" + return "kitchen_tableTOside_table" + elif state["location"] == "fridge": + if state["bread_at_fridge"] == "unknown": + return "detect" + return "fridgeTOkitchen_table" + + + def _log_action(self, state, new_state, action, start, end): + """Log an action to the mongoDB database. + + Args: + state: The predecessor state + new_state: The successor state + action: The executed action + start: The start time + end: The end time + """ + doc = {} + doc["run_id"] = self._run_id + doc["mode"] = self._mode + doc["option"] = action + doc["date_started"] = float(start.nanoseconds) / 1e9 + doc["date_finished"] = float(end.nanoseconds) / 1e9 + doc["duration"] = float((end - start).nanoseconds) / 1e9 + doc["_meta"] = {"inserted_at": datetime.now()} + + for sf in state._state_dict: + doc["{}0".format(sf)] = state[sf] + + for sf in new_state._state_dict: + doc["{}t".format(sf)] = new_state[sf] + + if self._mode == "data": + self._db_collection.insert_one(doc) + else: + self.get_logger().info(f"Finished {doc["option"]} in {doc["duration"]} seconds") + + def _search_for_bread(self, state): + """Check if the bread is at a location. + + Args: + state: The current state + + Returns: + The updated state + """ + detect_goal = ExecuteTaskAction.Goal() + detect_goal.action.robot = "robot" + detect_goal.action.type = "detect" + detect_goal.action.object = "bread" + self.get_logger().info(f"Trying to detect bread") + + future = self._pyrobosim_client.send_goal_async(detect_goal) + rclpy.spin_until_future_complete(self, future) + start = self.get_clock().now() + goal_handle = future.result() + if not goal_handle.accepted: + self.get_logger().error("Detect Action Not Accepted") + return None + + result = goal_handle.get_result_async() + rclpy.spin_until_future_complete(self, result) + end = self.get_clock().now() + + bread_found = result.result().result.execution_result.status == 0 + new_bread_status = "yes" if bread_found else "no" + self.get_logger().info(f"Bread found: {new_bread_status}") + + new_state_dict = {} + unknown_vars = [] + for sf in state._state_dict: + new_state_dict[state._sf_dict[sf]] = ( + new_bread_status + if sf == "bread_at_{}".format(state["location"]) + else state._state_dict[sf] + ) + if new_state_dict[state._sf_dict[sf]] == "unknown": + unknown_vars.append(state._sf_dict[sf]) + + fill_val = None + if new_bread_status == "yes": # We can fill in everything once a yes is found + fill_val = "no" + elif len(unknown_vars) == 1: # The last unknown is yes if all else is no + fill_val = "yes" + + if fill_val is not None: # Replace the unknowns + for sf in unknown_vars: + new_state_dict[sf] = fill_val + + new_state = State(new_state_dict) + self._log_action(state, new_state, "detect", start, end) + return new_state + + def _navigation(self, state, action): + """Navigate to a new location. + + Args: + state: The current state + action: The current navigation action (just the destination) + + Returns: + The updated state + """ + goal_loc = action.split("TO")[-1] + nav_goal = ExecuteTaskAction.Goal() + nav_goal.action.robot = "robot" + nav_goal.action.type = "navigate" + nav_goal.action.target_location = goal_loc + self.get_logger().info(f"Trying to navigate to {goal_loc}") + + future = self._pyrobosim_client.send_goal_async(nav_goal) + rclpy.spin_until_future_complete(self, future) + start = self.get_clock().now() + goal_handle = future.result() + if not goal_handle.accepted: + self.get_logger().error("Navigation Action Not Accepted") + return None + + result = goal_handle.get_result_async() + rclpy.spin_until_future_complete(self, result) + end = self.get_clock().now() + + # Nav may fail - we want to check the Execution result message + # End status 0 means success, anything else means failure + end_status = result.result().result.execution_result.status + if end_status != 0: + self.get_logger().error("Edge Navigation Action Failed") + new_state = state + else: + self.get_logger().info("Edge Navigation Action Successful") + new_state_dict = {} + for sf in state._state_dict: # Update location + new_state_dict[state._sf_dict[sf]] = ( + goal_loc if sf == "location" else state._state_dict[sf] + ) + new_state = State(new_state_dict) + + self._log_action(state, new_state, action, start, end) + return new_state + + def execute_policy(self): + """Execute the policy until a termination condition is satisfied.""" + + current_state = self._create_initial_state() + history = [current_state] + + while not self._goal_fn(history): + action = self._policy_fn(current_state) + assert action in self._enabled_actions(current_state) + + self.get_logger().info( + "Action: {}; Executing {} in {}".format( + len(history), action, current_state + ) + ) + + if action == "detect": + current_state = self._search_for_bread(current_state) + else: # Edge action + current_state = self._navigation(current_state, action) + + history.append(current_state) + + if current_state is None: # Error case + self.get_logger().error("Error During Policy Execution. Exiting.") + return + + +def main(args=None): + rclpy.init(args=args) + + policy_exec = PolicyExecutor() + + # Execute the policy and then shut down + policy_exec.execute_policy() + rclpy.shutdown() diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/resource/refine_plan_demo b/examples/overarching_tutorial/simulation/refine_plan_demo/resource/refine_plan_demo new file mode 100644 index 00000000..e69de29b diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/setup.cfg b/examples/overarching_tutorial/simulation/refine_plan_demo/setup.cfg new file mode 100644 index 00000000..245024a5 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/refine_plan_demo +[install] +install_scripts=$base/lib/refine_plan_demo diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/setup.py b/examples/overarching_tutorial/simulation/refine_plan_demo/setup.py new file mode 100644 index 00000000..4da64fa6 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/setup.py @@ -0,0 +1,53 @@ +from setuptools import find_packages, setup +from glob import glob +import os + +package_name = "refine_plan_demo" + + +def package_files(directory_list): + """Glob wasn't working how I intended, so I took this nice function from: + https://answers.ros.org/question/397319/how-to-copy-folders-with-subfolders-to-package-installation-path/ + """ + paths_dict = {} + data_files = [] + for directory in directory_list: + for path, _, filenames in os.walk(directory): + + for filename in filenames: + file_path = os.path.join(path, filename) + install_path = os.path.join("share", package_name, path) + if install_path in paths_dict.keys(): + paths_dict[install_path].append(file_path) + else: + paths_dict[install_path] = [file_path] + + for key in paths_dict.keys(): + data_files.append((key, paths_dict[key])) + return data_files + + +setup( + name=package_name, + version="0.0.0", + packages=find_packages(exclude=["test"]), + data_files=package_files(["params/"]) # Params needed to send over the policy + + [ + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), + ("share/" + package_name, ["package.xml"]), + ( + os.path.join("share", package_name, "launch"), + glob(os.path.join("launch", "*launch.[pxy][yma]*")), + ), + ], + install_requires=["setuptools"], + zip_safe=True, + maintainer="charlie", + maintainer_email="me@charliestreet.net", + description="A REFINE-PLAN demo in Pyrobosim for the CONVINCE project.", + license="Apache-2.0", + tests_require=["pytest"], + entry_points={ + "console_scripts": ["policy_executor = refine_plan_demo.policy_executor:main"], + }, +) diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_copyright.py b/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_copyright.py new file mode 100644 index 00000000..97a39196 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_flake8.py b/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_flake8.py new file mode 100644 index 00000000..27ee1078 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_pep257.py b/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_pep257.py new file mode 100644 index 00000000..b234a384 --- /dev/null +++ b/examples/overarching_tutorial/simulation/refine_plan_demo/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' diff --git a/examples/overarching_tutorial/simulation/requirements.txt b/examples/overarching_tutorial/simulation/requirements.txt index 8368e977..37da73b2 100644 --- a/examples/overarching_tutorial/simulation/requirements.txt +++ b/examples/overarching_tutorial/simulation/requirements.txt @@ -3,6 +3,7 @@ astar matplotlib numpy<2.1.0 pycollada +pymongo==4.8.0 PySide6>=6.7.1 pytest-dependency PyYAML diff --git a/examples/overarching_tutorial/simulation/tutorial_sim/tutorial_sim/run.py b/examples/overarching_tutorial/simulation/tutorial_sim/tutorial_sim/run.py index 5c2ab9c0..267ca875 100644 --- a/examples/overarching_tutorial/simulation/tutorial_sim/tutorial_sim/run.py +++ b/examples/overarching_tutorial/simulation/tutorial_sim/tutorial_sim/run.py @@ -18,13 +18,28 @@ def create_ros_node(): """Initializes ROS node""" rclpy.init() node = WorldROSWrapper(state_pub_rate=0.1, dynamics_rate=0.01) - + + # Allow for detect success probability to be overwritten + # This is for the REFINE-PLAN demo only + # I'd rather do this than change the world file + node.declare_parameter("detect_succ_prob", rclpy.Parameter.Type.DOUBLE) + detect_prob = node.get_parameter_or("detect_succ_prob", alternative_value=None) + world_file = os.path.join( get_package_share_directory("tutorial_sim"), "worlds", "world.yaml", ) world = WorldYamlLoader().from_file(world_file) + + if detect_prob is not None: + node.get_logger().info( + f"Overriding detect success probability to: {detect_prob.value}" + ) + robot_name = world.get_robot_names()[0] + robot = world.get_robot_by_name(robot_name) + robot.action_execution_options["detect"].success_probability = detect_prob.value + # world.reset(seed=0) world.reset() # This randomizes the world. node.set_world(world) diff --git a/examples/overarching_tutorial/simulation/tutorial_sim/worlds/world.yaml b/examples/overarching_tutorial/simulation/tutorial_sim/worlds/world.yaml index 83ae43ac..af880214 100644 --- a/examples/overarching_tutorial/simulation/tutorial_sim/worlds/world.yaml +++ b/examples/overarching_tutorial/simulation/tutorial_sim/worlds/world.yaml @@ -19,10 +19,9 @@ robots: path_executor: type: constant_velocity path_planner: - type: rrt - bidirectional: false - max_connection_dist: 0.5 - max_time: 10.0 + type: astar + grid_resolution: 0.05 + grid_inflation_radius: 0.05 action_execution_options: navigate: success_probability: 0.9 @@ -118,14 +117,14 @@ locations: category: table pose: position: - x: -2.75 + x: -2.7 y: 4.5 - name: side_table parent: dining category: table pose: position: - x: -4.25 + x: -4.3 y: 4.5 - name: kitchen_table parent: kitchen @@ -149,3 +148,7 @@ objects: category: snacks - parent: fridge category: butter + - category: bread + parent: + choices: [dining_table, fridge, kitchen_table, side_table] + probabilities: [0.5, 0.1, 0.1, 0.3]