diff --git a/.github/workflows/apk.yaml b/.github/workflows/apk.yaml index ea3e5768..9127ee7c 100644 --- a/.github/workflows/apk.yaml +++ b/.github/workflows/apk.yaml @@ -1,5 +1,3 @@ -# Check for lint error and auto correct them - name: Compile APK on: ['push', 'pull_request', 'workflow_dispatch'] diff --git a/.github/workflows/ci_pr.yml b/.github/workflows/ci_pr.yml index d768e72b..6a1f767b 100644 --- a/.github/workflows/ci_pr.yml +++ b/.github/workflows/ci_pr.yml @@ -7,27 +7,6 @@ on: jobs: - build-xenial: - - runs-on: ubuntu-20.04 - - steps: - - - name: Checkout Astrobee - uses: actions/checkout@v3 - with: - repository: nasa/astrobee - path: astrobee/ - - - name: Checkout ISAAC - uses: actions/checkout@v3 - with: - submodules: recursive - path: isaac/ - - - name: Build code for isaac:astrobee Ubuntu 16 - run: isaac/scripts/docker/build.sh --remote --xenial --astrobee-source-path astrobee/ - build-focal: runs-on: ubuntu-20.04 diff --git a/.github/workflows/ci_push.yml b/.github/workflows/ci_push.yml index c3fa9ed5..f5a49a6a 100644 --- a/.github/workflows/ci_push.yml +++ b/.github/workflows/ci_push.yml @@ -8,65 +8,6 @@ on: jobs: - build-xenial: - - runs-on: ubuntu-20.04 - - steps: - - - name: Checkout Astrobee - uses: actions/checkout@v3 - with: - repository: nasa/astrobee - path: astrobee/ - - - name: Checkout ISAAC - uses: actions/checkout@v3 - with: - submodules: recursive - path: isaac/ - - - name: Build code for isaac:astrobee Ubuntu 16 - run: docker build isaac -f isaac/scripts/docker/isaac_astrobee.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - --build-arg REMOTE=ghcr.io/nasa - -t ghcr.io/${{ github.repository_owner }}/isaac:latest-astrobee-ubuntu16.04 - - - name: Build code for isaac:latest Ubuntu 16 - run: docker build isaac -f isaac/scripts/docker/isaac.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - --build-arg REMOTE=ghcr.io/nasa - -t ghcr.io/${{ github.repository_owner }}/isaac:latest-ubuntu16.04 - - - name: Build messages dockers for Ubuntu 16 (astrobee) - run: docker build astrobee -f isaac/scripts/docker/astrobee_msgs.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - -t ghcr.io/${{ github.repository_owner }}/isaac:astrobee-msgs-ubuntu16.04 - - - name: Build messages dockers for Ubuntu 16 (isaac) - run: docker build isaac -f isaac/scripts/docker/isaac_msgs.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - --build-arg REMOTE=ghcr.io/nasa - -t ghcr.io/${{ github.repository_owner }}/isaac:msgs-ubuntu16.04 - - - name: Log in to registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin - - - name: Push Docker image - run: | - if [ "${{ github.repository_owner }}" = "nasa" ]; then docker push ghcr.io/${{ github.repository_owner }}/isaac:latest-astrobee-ubuntu16.04; fi; - if [ "${{ github.repository_owner }}" = "nasa" ]; then docker push ghcr.io/${{ github.repository_owner }}/isaac:latest-ubuntu16.04; fi; - if [ "${{ github.repository_owner }}" = "nasa" ]; then docker push ghcr.io/${{ github.repository_owner }}/isaac:astrobee-msgs-ubuntu16.04; fi; - if [ "${{ github.repository_owner }}" = "nasa" ]; then docker push ghcr.io/${{ github.repository_owner }}/isaac:msgs-ubuntu16.04; fi; - build-focal: runs-on: ubuntu-20.04 diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml index c7b48cc9..bcaaa749 100644 --- a/.github/workflows/ci_release.yml +++ b/.github/workflows/ci_release.yml @@ -8,68 +8,6 @@ on: jobs: - build-xenial: - - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v3 - - - name: Checkout Astrobee - uses: actions/checkout@v3 - with: - repository: nasa/astrobee - path: astrobee/ - - - name: Checkout ISAAC - uses: actions/checkout@v3 - with: - submodules: recursive - path: isaac/ - - - name: Build code for isaac:astrobee Ubuntu 16 - run: docker build isaac -f isaac/scripts/docker/isaac_astrobee.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - --build-arg REMOTE=ghcr.io/nasa - -t isaac/isaac:latest-astrobee-ubuntu16.04 - - - name: Build code for isaac:latest Ubuntu 16 - run: docker build isaac -f isaac/scripts/docker/isaac.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - --build-arg REMOTE=isaac - -t isaac/isaac:latest-ubuntu16.04 - - - name: Build messages dockers for Ubuntu 16 (astrobee) - run: docker build astrobee -f isaac/scripts/docker/astrobee_msgs.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - -t isaac/isaac:astrobee-msgs-ubuntu16.04 - - - name: Build messages dockers for Ubuntu 16 (isaac) - run: docker build isaac -f isaac/scripts/docker/isaac_msgs.Dockerfile - --build-arg UBUNTU_VERSION=16.04 - --build-arg ROS_VERSION=kinetic - --build-arg PYTHON='' - --build-arg REMOTE=isaac - -t isaac/isaac:msgs-ubuntu16.04 - - - name: Log in to registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin - - - name: Push Docker image - run: | - cd isaac - export VERSION=`grep -w -m 1 "Release" RELEASE.md | awk '{print $3}'` - docker tag isaac/isaac:latest-astrobee-ubuntu16.04 ghcr.io/${{ github.repository_owner }}/isaac:v${VERSION}-astrobee-ubuntu16.04 - if [ "${{ github.repository_owner }}" = "nasa" ]; then docker push ghcr.io/${{ github.repository_owner }}/isaac:v${VERSION}-astrobee-ubuntu16.04; fi; - docker tag isaac/isaac:latest-ubuntu16.04 ghcr.io/${{ github.repository_owner }}/isaac:v${VERSION}-ubuntu16.04 - if [ "${{ github.repository_owner }}" = "nasa" ]; then docker push ghcr.io/${{ github.repository_owner }}/isaac:v${VERSION}-ubuntu16.04; fi; - build-focal: runs-on: ubuntu-20.04 diff --git a/.gitmodules b/.gitmodules index b94bfb0e..6d14a890 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,11 @@ [submodule "isaac_msgs"] path = communications/isaac_msgs url = https://github.com/nasa/isaac_msgs.git +[submodule "astrobee/survey_manager/survey_planner/src/ros2_planning_system"] + path = astrobee/survey_manager/survey_planner/src/ros2_planning_system + url = https://github.com/bckempa/ros2_planning_system + branch = noetic-devel +[submodule "astrobee/survey_manager/survey_planner/src/ros1_lifecycle"] + path = astrobee/survey_manager/survey_planner/src/ros1_lifecycle + url = https://github.com/bckempa/ros1_lifecycle + branch = noetic-devel diff --git a/README.md b/README.md index 3383cae3..b0287805 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ the [Astrobee robot](https://github.com/nasa/astrobee). This repository includes - [Dense mapping](https://nasa.github.io/isaac/html/geometric_streaming_mapper.html) to create a textured 3D map - [Volumetric mapping](https://nasa.github.io/isaac/html/volumetric_mapper.html) to map volumetric signals, such as WiFi. - [Image analysis](https://nasa.github.io/isaac/html/ano.html) module to train a neural network to detect anomalies +- [Survey Manager](https://nasa.github.io/isaac/html/survey.html) semi-autonomous planning and execution of imaging tasks. You may also be interested in the separate repository for the [ISAAC User Interface](https://github.com/nasa/isaac_user_interface), which enables monitoring of multiple robots through a web browser. diff --git a/astrobee/readme.md b/astrobee/readme.md index 89e60ac6..3f2f50d3 100644 --- a/astrobee/readme.md +++ b/astrobee/readme.md @@ -9,4 +9,6 @@ The Astrobeemodule contains the ISAAC additions to the Astrobee FSW \subpage sim : Gazebo plugins for simulation including acoustic and heat camera, RFID reader and emmiter, as well as WiFi reader and emmiters. -\subpage gs_action_helper : Guest science action helper used to communicate with the ground through DDS messages \ No newline at end of file +\subpage gs_action_helper : Guest science action helper used to communicate with the ground through DDS messages + +\subpage survey : Semi-autonomous survey tasking of Astrobees. diff --git a/astrobee/survey_manager/readme.md b/astrobee/survey_manager/readme.md new file mode 100644 index 00000000..75b544a4 --- /dev/null +++ b/astrobee/survey_manager/readme.md @@ -0,0 +1,5 @@ +\page survey Survey Manager + +The ISAAC Survey Manager provides semi-autonomous planning and execution of panorama and stereophotography tasks to reduce operator loading and improve activity utilization. + +\subpage survey_planner : Planning and scheduling of queued survey actions. diff --git a/astrobee/survey_manager/survey_planner/behavior_trees/collecting_panoramas.xml b/astrobee/survey_manager/survey_planner/behavior_trees/collecting_panoramas.xml new file mode 100644 index 00000000..d8cb181f --- /dev/null +++ b/astrobee/survey_manager/survey_planner/behavior_trees/collecting_panoramas.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/astrobee/survey_manager/survey_planner/behavior_trees/move_to.xml b/astrobee/survey_manager/survey_planner/behavior_trees/move_to.xml new file mode 100644 index 00000000..a708b178 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/behavior_trees/move_to.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/astrobee/survey_manager/survey_planner/behavior_trees/move_to_inspect.xml b/astrobee/survey_manager/survey_planner/behavior_trees/move_to_inspect.xml new file mode 100644 index 00000000..b40db393 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/behavior_trees/move_to_inspect.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/astrobee/survey_manager/survey_planner/data/jem_survey_dynamic.yaml b/astrobee/survey_manager/survey_planner/data/jem_survey_dynamic.yaml new file mode 100644 index 00000000..680641ff --- /dev/null +++ b/astrobee/survey_manager/survey_planner/data/jem_survey_dynamic.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2023, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The "ISAAC - Integrated System for Autonomous and Adaptive Caretaking +# platform" software is 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. + +# Example dynamic configuration info used when generating a PDDL problem. For now, this is goal +# conditions and initial state. A likely conops is that the initial version of this file for a +# specific activity would be hand-generated, but it might later be automatically regenerated by the +# survey manager when a replan is needed (remove completed/failed goals, add retry goals, update +# initial state to match actual current state, etc.) See also jem_survey_static.yaml. + +goals: + +- {type: panorama, robot: bumble, order: 0, location: jem_bay4, run: 1} +- {type: panorama, robot: bumble, order: 1, location: jem_bay3, run: 1} +- {type: panorama, robot: bumble, order: 2, location: jem_bay2, run: 1} +- {type: panorama, robot: bumble, order: 3, location: jem_bay1, run: 1} +- {type: stereo, robot: bumble, order: 4, trajectory: jem_bay1_to_bay3, run: 1} + +# We want Bumble to return to its berth at the end of the run, but adding this goal causes POPF to +# get confused and greatly increase the total run time. For some reason, it doesn't notice it can +# use the same plan as without this goal and then add some motion actions at the end to achieve this +# goal. Instead, it falls back to only undocking one robot at a time, which slows things down by +# about 2x. +# - {type: robot_at, robot: bumble, location: berth1} + +- {type: panorama, robot: honey, order: 0, location: jem_bay7, run: 1} +- {type: panorama, robot: honey, order: 1, location: jem_bay6, run: 1} +- {type: panorama, robot: honey, order: 2, location: jem_bay5, run: 1} + +# This is another objective we want to include that for some reason causes POPF to fail to generate +# a plan (hang indefinitely). No obvious reason why it should cause a problem. +# - {type: stereo, robot: honey, order: 3, trajectory: jem_bay4_to_bay7, run: 1} + +- {type: robot_at, robot: honey, location: berth2} + +init: + bumble: + location: berth1 + honey: + location: berth2 diff --git a/astrobee/survey_manager/survey_planner/data/jem_survey_static.yaml b/astrobee/survey_manager/survey_planner/data/jem_survey_static.yaml new file mode 100644 index 00000000..db617126 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/data/jem_survey_static.yaml @@ -0,0 +1,59 @@ +# Copyright (c) 2023, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The "ISAAC - Integrated System for Autonomous and Adaptive Caretaking +# platform" software is 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. + +# Static configuration info used when generating a PDDL problem and also when executing actions in a +# PDDL plan. This info should be static in the sense that it nominally doesn't change during an ISS +# activity, so the survey manager doesn't have to modify it. However, an edge case is that an +# operator might want to manually edit something in here (like add a new symbolic location or nudge +# the position of a named bay away from an obstacle) and restart the survey manager. On the other +# hand, info that is *expected* to change as part of the survey manager conops belongs in +# jem_survey_dynamic.yaml. + +# Useful reference for positions and stereo survey trajectories: +# https://babelfish.arc.nasa.gov/confluence/display/FFOPS/ISAAC+Phase+1X+Activity+9+Ground+Procedure + +bays: + # 3D coordinates for symbolic bays in ISS Analysis Coordinate System used by Astrobee + jem_bay1: [11.0, -4.0, 4.8] + jem_bay2: [11.0, -5.0, 4.8] + jem_bay3: [11.0, -6.0, 4.8] + jem_bay4: [11.0, -7.0, 4.8] + jem_bay5: [11.0, -8.0, 4.8] + jem_bay6: [11.0, -9.0, 4.8] + jem_bay7: [11.0, -9.7, 4.8] + +bogus_bays: [jem_bay0, jem_bay8] +berths: [berth1, berth2] +robots: [bumble, honey] +num_orders: 10 + +stereo: + # Meta-data about stereo survey options + jem_bay1_to_bay3: + # fplan: Name of external fplan specification of trajectory in astrobee_ops/gds/plans/ISAAC/ + fplan: "jem_stereo_mapping_bay1_to_bay3.fplan" + # base_location: Where trajectory starts and ends for planning purposes (rough location, not exact) + base_location: jem_bay1 + # bound_location: The other end of the interval covered by the trajectory, for planner collision + # check purposes. (Note a trajectory may fly a bit into a bay that it doesn't claim to cover. + # The two surveys that cover the module purposefully overlap.) + bound_location: jem_bay4 + jem_bay4_to_bay7: + fplan: "jem_stereo_mapping_bay4_to_bay7.fplan" + base_location: jem_bay7 + bound_location: jem_bay4 diff --git a/astrobee/survey_manager/survey_planner/data/sample_output_plan.txt b/astrobee/survey_manager/survey_planner/data/sample_output_plan.txt new file mode 100644 index 00000000..6a119031 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/data/sample_output_plan.txt @@ -0,0 +1,31 @@ +$ time popf domain_survey.pddl problem_jem_survey.pddl +Constructing lookup tables: [10%] [20%] [30%] [40%] [50%] [60%] [70%] [80%] [90%] [100%] +Post filtering unreachable actions: [10%] [20%] [30%] [40%] [50%] [60%] [70%] [80%] [90%] [100%] +3% of the ground temporal actions in this problem are compression-safe +b (25.000 | 30.000)b (24.000 | 50.001)b (23.000 | 70.002)b (22.000 | 90.003)b (21.000 | 990.004)b (20.000 | 990.004)b (19.000 | 1010.005)b (18.000 | 1910.006)b (17.000 | 1910.006)b (16.000 | 1930.007)b (15.000 | 2830.008)b (14.000 | 2830.008)b (13.000 | 2850.009)b (12.000 | 3750.010)b (11.000 | 3750.010)b (10.000 | 4350.011)b (9.000 | 4350.011)b (8.000 | 4350.011)b (7.000 | 4350.011)b (6.000 | 4350.011)b (5.000 | 4350.011)b (4.000 | 5270.013)b (3.000 | 5270.013)b (2.000 | 5290.014)b (1.000 | 5310.015);;;; Solution Found +; Time 0.56 +0.000: (undock bumble berth1 jem_bay7 jem_bay8 jem_bay6) [30.000] +30.001: (move bumble jem_bay7 jem_bay6 jem_bay5) [20.000] +50.002: (move bumble jem_bay6 jem_bay5 jem_bay4) [20.000] +70.003: (move bumble jem_bay5 jem_bay4 jem_bay3) [20.000] +70.003: (undock honey berth2 jem_bay7 jem_bay8 jem_bay6) [30.000] +90.004: (panorama bumble o0 jem_bay4 run1) [900.000] +100.004: (panorama honey o0 jem_bay7 run1) [900.000] +990.005: (move bumble jem_bay4 jem_bay3 jem_bay2) [20.000] +1000.005: (move honey jem_bay7 jem_bay6 jem_bay5) [20.000] +1010.006: (panorama bumble o1 jem_bay3 run1) [900.000] +1020.006: (panorama honey o1 jem_bay6 run1) [900.000] +1910.007: (move bumble jem_bay3 jem_bay2 jem_bay1) [20.000] +1930.008: (panorama bumble o2 jem_bay2 run1) [900.000] +2830.009: (move bumble jem_bay2 jem_bay1 jem_bay0) [20.000] +2850.010: (panorama bumble o3 jem_bay1 run1) [900.000] +3750.011: (stereo bumble o4 jem_bay1 jem_bay4 jem_bay5 jem_bay3 run1) [600.000] +4350.012: (move honey jem_bay6 jem_bay5 jem_bay4) [20.000] +4370.013: (panorama honey o2 jem_bay5 run1) [900.000] +5270.014: (move honey jem_bay5 jem_bay6 jem_bay7) [20.000] +5290.015: (move honey jem_bay6 jem_bay7 jem_bay8) [20.000] +5310.016: (dock honey jem_bay7 berth2) [30.000] + +real 0m0.585s +user 0m0.553s +sys 0m0.032s diff --git a/astrobee/survey_manager/survey_planner/data/sample_output_plan.yaml b/astrobee/survey_manager/survey_planner/data/sample_output_plan.yaml new file mode 100644 index 00000000..d17b7e1c --- /dev/null +++ b/astrobee/survey_manager/survey_planner/data/sample_output_plan.yaml @@ -0,0 +1,200 @@ +- start_time_seconds: '0.000' + action: + type: undock + robot: bumble + duration_seconds: '30.000' +- start_time_seconds: '30.001' + action: + type: move + robot: bumble + to_name: jem_bay6 + to_pos: + - 11.0 + - -9.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '50.002' + action: + type: move + robot: bumble + to_name: jem_bay5 + to_pos: + - 11.0 + - -8.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '70.003' + action: + type: move + robot: bumble + to_name: jem_bay4 + to_pos: + - 11.0 + - -7.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '70.003' + action: + type: undock + robot: honey + duration_seconds: '30.000' +- start_time_seconds: '90.004' + action: + type: panorama + robot: bumble + location_name: jem_bay4 + location_pos: + - 11.0 + - -7.0 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '100.004' + action: + type: panorama + robot: honey + location_name: jem_bay7 + location_pos: + - 11.0 + - -9.7 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '990.005' + action: + type: move + robot: bumble + to_name: jem_bay3 + to_pos: + - 11.0 + - -6.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '1000.005' + action: + type: move + robot: honey + to_name: jem_bay6 + to_pos: + - 11.0 + - -9.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '1010.006' + action: + type: panorama + robot: bumble + location_name: jem_bay3 + location_pos: + - 11.0 + - -6.0 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '1020.006' + action: + type: panorama + robot: honey + location_name: jem_bay6 + location_pos: + - 11.0 + - -9.0 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '1910.007' + action: + type: move + robot: bumble + to_name: jem_bay2 + to_pos: + - 11.0 + - -5.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '1930.008' + action: + type: panorama + robot: bumble + location_name: jem_bay2 + location_pos: + - 11.0 + - -5.0 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '2830.009' + action: + type: move + robot: bumble + to_name: jem_bay1 + to_pos: + - 11.0 + - -4.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '2850.010' + action: + type: panorama + robot: bumble + location_name: jem_bay1 + location_pos: + - 11.0 + - -4.0 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '3750.011' + action: + type: stereo + robot: bumble + fplan: jem_stereo_mapping_bay1_to_bay3.fplan + run: 1 + duration_seconds: '600.000' +- start_time_seconds: '4350.012' + action: + type: move + robot: honey + to_name: jem_bay5 + to_pos: + - 11.0 + - -8.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '4370.013' + action: + type: panorama + robot: honey + location_name: jem_bay5 + location_pos: + - 11.0 + - -8.0 + - 4.8 + run: 1 + duration_seconds: '900.000' +- start_time_seconds: '5270.014' + action: + type: move + robot: honey + to_name: jem_bay6 + to_pos: + - 11.0 + - -9.0 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '5290.015' + action: + type: move + robot: honey + to_name: jem_bay7 + to_pos: + - 11.0 + - -9.7 + - 4.8 + duration_seconds: '20.000' +- start_time_seconds: '5310.016' + action: + type: dock + robot: honey + berth: berth2 + duration_seconds: '30.000' diff --git a/astrobee/survey_manager/survey_planner/pddl/domain_survey.pddl b/astrobee/survey_manager/survey_planner/pddl/domain_survey.pddl new file mode 100644 index 00000000..e31999fd --- /dev/null +++ b/astrobee/survey_manager/survey_planner/pddl/domain_survey.pddl @@ -0,0 +1,331 @@ +(define (domain survey-manager) + (:requirements + :strips + :typing + :durative-actions + :fluents + ) + + (:types + location + robot + order + run-number + ) + + (:predicates + ;; === Static predicates === + ;; move-connected: Indicates a robot can travel from ?from to ?to using a single + ;; move action. Note that these are interpreted as directed links, so for the usual case + ;; that you can travel in either direction, you must assert the predicate both ways. + ;; Neither location can be a berth (use the dock-connected predicate for that). However, + ;; both bays 6 and 7 should be move-connected to both berth approach points. + (move-connected ?from ?to - location) + + ;; location-real: Indicates a location is a real place the robot can fly to. We've added + ;; bogus locations bay0 and bay8 to the problem instance to satisfy the implicit assumption + ;; of the collision checking that every bay has two neighbors. We assert location-real for + ;; the other locations and use it as a precondition on moves so the planner can't + ;; accidentally fly to a bogus location. + (location-real ?location - location) + + ;; dock-connected: Indicates a robot can move from ?approach to ?berth using a dock + ;; action, or from ?berth to ?approach using an undock action. + (dock-connected ?approach ?berth - location) + + ;; robots-different: Indicates a != b. Needs to be expressed as a positive predicate + ;; so it can be used as a precondition. Must be asserted in both directions. + (robots-different ?a ?b - robot) + + ;; locations-different: Indicates a != b. Needs to be expressed as a positive predicate + ;; so it can be used as a precondition. Must be asserted in both directions. + (locations-different ?a ?b - location) + + ;; === Dynamic predicates === + ;; robot-available: Since a robot can only perform one action at a time in our domain, each + ;; action grabs this mutex. In the initial state, both robots should be available. + (robot-available ?robot - robot) + + ;; robot-at: Indicates the robot's current position (usually not set during execution of + ;; motion actions). In the initial state, both robots should have robot-at set for their + ;; initial locations. + (robot-at ?robot - robot ?location - location) + + ;; location-available: Indicates that no robot has reserved the location. When stationary, + ;; each robot reserves its current location. During a move, it reserves both its ?from + ;; location and its ?to location. Collision avoidance checks prevent robots from reserving + ;; the same location or reserving adjacent bays while flying. Note: It might be more natural + ;; to express this as a location-reserved predicate with the opposite boolean sense, but we + ;; need it to be this way so we can use it as a precondition without negating it. In the + ;; initial state, we must mark location-available for all locations, real or bogus, except + ;; the initial robot locations. + (location-available ?location - location) + + ;; need-stereo: If you add a completed-stereo goal, you must also add a need-stereo + ;; predicate with identical parameters to the initial state. This is part of a hack that + ;; greatly improves planner performance. The need-stereo predicate has been made part of the + ;; preconditions of the stereo action, and one of its effects is to clear the + ;; predicate. Therefore, the planner won't waste time trying to execute stereo actions that + ;; the user didn't explicitly request. Without this hack, the planner run time blows up. + (need-stereo ?robot - robot ?order - order ?base ?bound - location ?run-number - run-number) + + ;; === Goal predicates === + ;; completed-panorama: The goal to add if you want the plan to include collecting a + ;; panorama. For now, goals specify ?robot and ?order parameters that constrain + ;; multi-robot task allocation and task ordering. The ?run-number is used to indicate + ;; retries and is meaningless to the planner but helpful for post-run analysis. + (completed-panorama + ?robot - robot + ?order - order + ?location - location + ?run-number - run-number + ) + + ;; completed-stereo: The goal to add if you want the plan to include collecting a stereo + ;; survey. For now, goals specify ?robot and ?order parameters that constrain multi-robot + ;; task allocation and task ordering. The ?run-number is used to indicate retries and is + ;; meaningless to the planner but helpful for post-run analysis. The current model for + ;; stereo surveys assumes the robot starts and ends the survey at the same location called + ;; ?base (these locations only need to be same to the effective precision modeled in the + ;; planner, "less than a bay apart"). The ?bound argument indicates the other end of the + ;; interval covered by the survey and is used for collision checking. It's assumed that + ;; ?base and ?bound are not adjacent locations. If future stereo surveys violate these + ;; assumptions the model will need to be revisited. + (completed-stereo + ?robot - robot + ?order - order + ?base ?bound - location + ?run-number - run-number + ) + ) + + (:functions + ;; === Static numeric fluents === + ;; order-identity: An identity operator that maps from a symbolic order like o0 to its + ;; corresponding numeric value 0. + (order-identity ?order - order) + + ;; === Dynamic numeric fluents === + ;; robot-order: Indicates the order of the last action executed by ?robot. Later actions + ;; must not have a lower ?order (this only applies to the panorama and stereo actions that + ;; take an ?order parameter). In the initial state, each robot must have order -1. + (robot-order ?robot - robot) + ) + + (:durative-action dock + :parameters (?robot - robot ?from ?to - location) ;; from bay7 to berth1 or berth2 + :duration (= ?duration 30) + :condition + (and + ;; Check robot mutex + (at start (robot-available ?robot)) + + ;; Check parameters make sense + (at start (robot-at ?robot ?from)) + (at start (dock-connected ?from ?to)) + + ;; Check collision avoidance + (at start (location-available ?to)) + ; Don't need to check berth neighbors + ) + :effect + (and + ;; Grab and release robot mutex + (at start (not (robot-available ?robot))) + (at end (robot-available ?robot)) + + ;; Grab and release reserved locations + (at start (not (location-available ?to))) + (at end (location-available ?from)) + + ;; Update robot location + (at start (not (robot-at ?robot ?from))) + (at end (robot-at ?robot ?to)) + ) + ) + + (:durative-action undock + :parameters ( + ?robot - robot + ?from ?to - location ;; from berth1 or berth2 to bay7 + ?check1 ?check2 - location ;; neighbors of ?to to check for collision avoidance + ) + :duration (= ?duration 30) + :condition + (and + ;; Check robot mutex + (at start (robot-available ?robot)) + + ;; Check parameters make sense + (at start (robot-at ?robot ?from)) + (at start (dock-connected ?to ?from)) + (at start (location-real ?to)) + + ;(at start (robot-can-undock-now ?robot)) + + ;; Check collision avoidance + (at start (location-available ?to)) + (at start (locations-different ?check1 ?check2)) + (at start (move-connected ?check1 ?to)) + (at start (move-connected ?check2 ?to)) + (at start (location-available ?check1)) + (at start (location-available ?check2)) + ) + :effect + (and + ;; Grab and release robot mutex + (at start (not (robot-available ?robot))) + (at end (robot-available ?robot)) + + ;; Grab and release reserved locations + (at start (not (location-available ?to))) + (at end (location-available ?from)) + + ;; Update robot location + (at start (not (robot-at ?robot ?from))) + (at end (robot-at ?robot ?to)) + ) + ) + + (:durative-action move + :parameters ( + ?robot - robot + ?from ?to - location + ?check - location ;; neighbor of ?to to check for collision avoidance + ) + :duration (= ?duration 20) + :condition + (and + ;; Check robot mutex + (at start (robot-available ?robot)) + + ;; Check parameters make sense + (at start (robot-at ?robot ?from)) + (at start (move-connected ?from ?to)) + (at start (location-real ?to)) + + ;; Check collision avoidance + (at start (location-available ?to)) + ;; In general when flying to ?to we collision check whether ?to or either of its + ;; neighbors are reserved (by the other robot). In this case, one of the neighbors + ;; of ?to is ?from, which can't be reserved by the other robot since this robot + ;; previously reserved it. Therefore, we only need to check if the other neighbor + ;; (?check) is reserved. + (at start (locations-different ?check ?from)) + (at start (move-connected ?check ?to)) + (at start (location-available ?check)) + ) + :effect + (and + ;; Grab and release robot mutex + (at start (not (robot-available ?robot))) + (at end (robot-available ?robot)) + + ;; Grab and release reserved locations + (at start (not (location-available ?to))) + (at end (location-available ?from)) + + ;; Update robot location + (at start (not (robot-at ?robot ?from))) + (at end (robot-at ?robot ?to)) + ) + ) + + (:durative-action panorama + :parameters + ( + ?robot - robot + ?order - order + ?location - location + ?run-number - run-number + ) + ;; ~13 minutes, per https://babelfish.arc.nasa.gov/confluence/display/FFOPS/ISAAC+Phase+1X+Activity+9+Ground+Procedure + :duration (= ?duration 780) + :condition + (and + ;; Check robot mutex + (at start (robot-available ?robot)) + + ;; Check order + (at start (< (robot-order ?robot) (order-identity ?order))) + + ;; Check parameters make sense + (at start (robot-at ?robot ?location)) + ) + :effect + (and + ;; Grab and release robot mutex + (at start (not (robot-available ?robot))) + (at end (robot-available ?robot)) + + ;; Update order + (at end (assign (robot-order ?robot) (order-identity ?order))) + + ;; Mark success + (at end (completed-panorama ?robot ?order ?location ?run-number)) + ) + ) + + (:durative-action stereo + :parameters + ( + ?robot - robot + ?order - order + ;; ?base: The bay where we start and also stop. ?bound: The other end of the survey + ?base ?bound - location + ;; ?check1 and ?check2: Planner-selected neighbors of ?bound for collision check + ?check1 ?check2 - location + ?run-number - run-number + ) + :duration (= ?duration 600) ;; 10 minutes + :condition + (and + ;; Check robot mutex + (at start (robot-available ?robot)) + + ;; Check order + (at start (< (robot-order ?robot) (order-identity ?order))) + + ;; Check parameters make sense + (at start (robot-at ?robot ?base)) + (at start (location-real ?bound)) + + ;; Check for need-stereo so the planner only tries this action when the user + ;; explicitly requests it. + (at start (need-stereo ?robot ?order ?base ?bound ?run-number)) + + ;; Check collision avoidance + (at start (location-available ?bound)) + (at start (locations-different ?check1 ?check2)) + (at start (move-connected ?check1 ?bound)) + (at start (move-connected ?check2 ?bound)) + (at start (location-available ?check1)) + (at start (location-available ?check2)) + ) + :effect + (and + ;; Grab and release robot mutex + (at start (not (robot-available ?robot))) + (at end (robot-available ?robot)) + + ;; Update order + (at end (assign (robot-order ?robot) (order-identity ?order))) + + ;; Grab and release reserved locations + (at start (not (location-available ?bound))) + (at end (location-available ?bound)) + + ;; Update robot location - technically correct but not really needed + ;(at start (not (robot-at ?robot ?base))) + ;(at end (robot-at ?robot ?base)) + + ;; Clear need-stereo so the planner won't try to use the stereo action + ;; again after the user request is satisfied. + (at end (not (need-stereo ?robot ?order ?base ?bound ?run-number))) + + ;; Mark success + (at end (completed-stereo ?robot ?order ?base ?bound ?run-number)) + ) + ) + +) diff --git a/astrobee/survey_manager/survey_planner/pddl/jem_survey_template.pddl b/astrobee/survey_manager/survey_planner/pddl/jem_survey_template.pddl new file mode 100644 index 00000000..313ca28f --- /dev/null +++ b/astrobee/survey_manager/survey_planner/pddl/jem_survey_template.pddl @@ -0,0 +1,57 @@ +{{ header }} +(define (problem jem-survey) + (:domain survey-manager) + (:metric minimize (total-time)) + (:objects + {{ bays }} {{ berths }} - location + {{ robots }} - robot + {{ orders }} - order + run1 run2 run3 run4 run5 - run-number + ) + + (:goal + (and + {{ goals }} + ) + ) + + (:init + ;; === Static predicates === + {{ move_connected_predicates }} + + {{ location_real_predicates }} + + {{ dock_connected_predicates }} + + {{ robots_different_predicates }} + + {{ locations_different_predicates }} + + ;; === Dynamic predicates === + {{ robot_available_predicates }} + + {{ robot_at_predicates }} + + {{ location_available_predicates }} + + ;; need-stereo predicates must be asserted with identical parameters to the + ;; stereo-completed goals. See the need-stereo docs for more. + {{ need_stereo_predicates }} + + ;; === Static numeric fluents === + {{ order_identity_fluents }} + + ;; === Dynamic numeric fluents === + {{ robot_order_fluents }} + ) ;; end :init +) ;; end problem + +;; Include raw high-level config in problem in case an (ISAAC-custom) planner prefers to use it. + +;; BEGIN CONFIG_DYNAMIC +{{ config_dynamic }} +;; END CONFIG_DYNAMIC + +;; BEGIN CONFIG_STATIC +{{ config_static }} +;; END CONFIG_STATIC diff --git a/astrobee/survey_manager/survey_planner/pddl/problem_jem_survey.pddl b/astrobee/survey_manager/survey_planner/pddl/problem_jem_survey.pddl new file mode 100644 index 00000000..301f8c05 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/pddl/problem_jem_survey.pddl @@ -0,0 +1,296 @@ +;; Auto-generated by problem_generator.py. Do not edit! +;; Problem template: jem_survey_template.pddl +;; Dynamic config: jem_survey_dynamic.yaml +;; Static config: jem_survey_static.yaml + +(define (problem jem-survey) + (:domain survey-manager) + (:metric minimize (total-time)) + (:objects + jem_bay0 jem_bay1 jem_bay2 jem_bay3 jem_bay4 jem_bay5 jem_bay6 jem_bay7 jem_bay8 berth1 berth2 - location + bumble honey - robot + o0 o1 o2 o3 o4 o5 o6 o7 o8 o9 - order + run1 run2 run3 run4 run5 - run-number + ) + + (:goal + (and + (completed-panorama bumble o0 jem_bay4 run1) + (completed-panorama bumble o1 jem_bay3 run1) + (completed-panorama bumble o2 jem_bay2 run1) + (completed-panorama bumble o3 jem_bay1 run1) + (completed-stereo bumble o4 jem_bay1 jem_bay4 run1) + (completed-panorama honey o0 jem_bay7 run1) + (completed-panorama honey o1 jem_bay6 run1) + (completed-panorama honey o2 jem_bay5 run1) + (robot-at honey berth2) + ) + ) + + (:init + ;; === Static predicates === + (move-connected jem_bay0 jem_bay1) + (move-connected jem_bay1 jem_bay0) + (move-connected jem_bay1 jem_bay2) + (move-connected jem_bay2 jem_bay1) + (move-connected jem_bay2 jem_bay3) + (move-connected jem_bay3 jem_bay2) + (move-connected jem_bay3 jem_bay4) + (move-connected jem_bay4 jem_bay3) + (move-connected jem_bay4 jem_bay5) + (move-connected jem_bay5 jem_bay4) + (move-connected jem_bay5 jem_bay6) + (move-connected jem_bay6 jem_bay5) + (move-connected jem_bay6 jem_bay7) + (move-connected jem_bay7 jem_bay6) + (move-connected jem_bay7 jem_bay8) + (move-connected jem_bay8 jem_bay7) + + (location-real jem_bay1) + (location-real jem_bay2) + (location-real jem_bay3) + (location-real jem_bay4) + (location-real jem_bay5) + (location-real jem_bay6) + (location-real jem_bay7) + + (dock-connected jem_bay7 berth1) + (dock-connected jem_bay7 berth2) + + (robots-different bumble honey) + (robots-different honey bumble) + + (locations-different jem_bay0 jem_bay1) + (locations-different jem_bay0 jem_bay2) + (locations-different jem_bay0 jem_bay3) + (locations-different jem_bay0 jem_bay4) + (locations-different jem_bay0 jem_bay5) + (locations-different jem_bay0 jem_bay6) + (locations-different jem_bay0 jem_bay7) + (locations-different jem_bay0 jem_bay8) + (locations-different jem_bay1 jem_bay0) + (locations-different jem_bay1 jem_bay2) + (locations-different jem_bay1 jem_bay3) + (locations-different jem_bay1 jem_bay4) + (locations-different jem_bay1 jem_bay5) + (locations-different jem_bay1 jem_bay6) + (locations-different jem_bay1 jem_bay7) + (locations-different jem_bay1 jem_bay8) + (locations-different jem_bay2 jem_bay0) + (locations-different jem_bay2 jem_bay1) + (locations-different jem_bay2 jem_bay3) + (locations-different jem_bay2 jem_bay4) + (locations-different jem_bay2 jem_bay5) + (locations-different jem_bay2 jem_bay6) + (locations-different jem_bay2 jem_bay7) + (locations-different jem_bay2 jem_bay8) + (locations-different jem_bay3 jem_bay0) + (locations-different jem_bay3 jem_bay1) + (locations-different jem_bay3 jem_bay2) + (locations-different jem_bay3 jem_bay4) + (locations-different jem_bay3 jem_bay5) + (locations-different jem_bay3 jem_bay6) + (locations-different jem_bay3 jem_bay7) + (locations-different jem_bay3 jem_bay8) + (locations-different jem_bay4 jem_bay0) + (locations-different jem_bay4 jem_bay1) + (locations-different jem_bay4 jem_bay2) + (locations-different jem_bay4 jem_bay3) + (locations-different jem_bay4 jem_bay5) + (locations-different jem_bay4 jem_bay6) + (locations-different jem_bay4 jem_bay7) + (locations-different jem_bay4 jem_bay8) + (locations-different jem_bay5 jem_bay0) + (locations-different jem_bay5 jem_bay1) + (locations-different jem_bay5 jem_bay2) + (locations-different jem_bay5 jem_bay3) + (locations-different jem_bay5 jem_bay4) + (locations-different jem_bay5 jem_bay6) + (locations-different jem_bay5 jem_bay7) + (locations-different jem_bay5 jem_bay8) + (locations-different jem_bay6 jem_bay0) + (locations-different jem_bay6 jem_bay1) + (locations-different jem_bay6 jem_bay2) + (locations-different jem_bay6 jem_bay3) + (locations-different jem_bay6 jem_bay4) + (locations-different jem_bay6 jem_bay5) + (locations-different jem_bay6 jem_bay7) + (locations-different jem_bay6 jem_bay8) + (locations-different jem_bay7 jem_bay0) + (locations-different jem_bay7 jem_bay1) + (locations-different jem_bay7 jem_bay2) + (locations-different jem_bay7 jem_bay3) + (locations-different jem_bay7 jem_bay4) + (locations-different jem_bay7 jem_bay5) + (locations-different jem_bay7 jem_bay6) + (locations-different jem_bay7 jem_bay8) + (locations-different jem_bay8 jem_bay0) + (locations-different jem_bay8 jem_bay1) + (locations-different jem_bay8 jem_bay2) + (locations-different jem_bay8 jem_bay3) + (locations-different jem_bay8 jem_bay4) + (locations-different jem_bay8 jem_bay5) + (locations-different jem_bay8 jem_bay6) + (locations-different jem_bay8 jem_bay7) + + ;; === Dynamic predicates === + (robot-available bumble) + (robot-available honey) + + (robot-at bumble berth1) + (robot-at honey berth2) + + (location-available jem_bay0) + (location-available jem_bay1) + (location-available jem_bay2) + (location-available jem_bay3) + (location-available jem_bay4) + (location-available jem_bay5) + (location-available jem_bay6) + (location-available jem_bay7) + (location-available jem_bay8) + + ;; need-stereo predicates must be asserted with identical parameters to the + ;; stereo-completed goals. See the need-stereo docs for more. + (need-stereo bumble o4 jem_bay1 jem_bay4 run1) + + ;; === Static numeric fluents === + (= (order-identity o0) 0) + (= (order-identity o1) 1) + (= (order-identity o2) 2) + (= (order-identity o3) 3) + (= (order-identity o4) 4) + (= (order-identity o5) 5) + (= (order-identity o6) 6) + (= (order-identity o7) 7) + (= (order-identity o8) 8) + (= (order-identity o9) 9) + + ;; === Dynamic numeric fluents === + (= (robot-order bumble) -1) + (= (robot-order honey) -1) + ) ;; end :init +) ;; end problem + +;; Include raw high-level config in problem in case an (ISAAC-custom) planner prefers to use it. + +;; BEGIN CONFIG_DYNAMIC +;; # Copyright (c) 2023, United States Government, as represented by the +;; # Administrator of the National Aeronautics and Space Administration. +;; # +;; # All rights reserved. +;; # +;; # The "ISAAC - Integrated System for Autonomous and Adaptive Caretaking +;; # platform" software is 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. +;; +;; # Example dynamic configuration info used when generating a PDDL problem. For now, this is goal +;; # conditions and initial state. A likely conops is that the initial version of this file for a +;; # specific activity would be hand-generated, but it might later be automatically regenerated by the +;; # survey manager when a replan is needed (remove completed/failed goals, add retry goals, update +;; # initial state to match actual current state, etc.) See also jem_survey_static.yaml. +;; +;; goals: +;; +;; - {type: panorama, robot: bumble, order: 0, location: jem_bay4, run: 1} +;; - {type: panorama, robot: bumble, order: 1, location: jem_bay3, run: 1} +;; - {type: panorama, robot: bumble, order: 2, location: jem_bay2, run: 1} +;; - {type: panorama, robot: bumble, order: 3, location: jem_bay1, run: 1} +;; - {type: stereo, robot: bumble, order: 4, trajectory: jem_bay1_to_bay3, run: 1} +;; +;; # We want Bumble to return to its berth at the end of the run, but adding this goal causes POPF to +;; # get confused and greatly increase the total run time. For some reason, it doesn't notice it can +;; # use the same plan as without this goal and then add some motion actions at the end to achieve this +;; # goal. Instead, it falls back to only undocking one robot at a time, which slows things down by +;; # about 2x. +;; # - {type: robot_at, robot: bumble, location: berth1} +;; +;; - {type: panorama, robot: honey, order: 0, location: jem_bay7, run: 1} +;; - {type: panorama, robot: honey, order: 1, location: jem_bay6, run: 1} +;; - {type: panorama, robot: honey, order: 2, location: jem_bay5, run: 1} +;; +;; # This is another objective we want to include that for some reason causes POPF to fail to generate +;; # a plan (hang indefinitely). No obvious reason why it should cause a problem. +;; # - {type: stereo, robot: honey, order: 3, trajectory: jem_bay4_to_bay7, run: 1} +;; +;; - {type: robot_at, robot: honey, location: berth2} +;; +;; init: +;; bumble: +;; location: berth1 +;; honey: +;; location: berth2 +;; +;; END CONFIG_DYNAMIC + +;; BEGIN CONFIG_STATIC +;; # Copyright (c) 2023, United States Government, as represented by the +;; # Administrator of the National Aeronautics and Space Administration. +;; # +;; # All rights reserved. +;; # +;; # The "ISAAC - Integrated System for Autonomous and Adaptive Caretaking +;; # platform" software is 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. +;; +;; # Static configuration info used when generating a PDDL problem and also when executing actions in a +;; # PDDL plan. This info should be static in the sense that it nominally doesn't change during an ISS +;; # activity, so the survey manager doesn't have to modify it. However, an edge case is that an +;; # operator might want to manually edit something in here (like add a new symbolic location or nudge +;; # the position of a named bay away from an obstacle) and restart the survey manager. On the other +;; # hand, info that is *expected* to change as part of the survey manager conops belongs in +;; # jem_survey_dynamic.yaml. +;; +;; # Useful reference for positions and stereo survey trajectories: +;; # https://babelfish.arc.nasa.gov/confluence/display/FFOPS/ISAAC+Phase+1X+Activity+9+Ground+Procedure +;; +;; bays: +;; # 3D coordinates for symbolic bays in ISS Analysis Coordinate System used by Astrobee +;; jem_bay1: [11.0, -4.0, 4.8] +;; jem_bay2: [11.0, -5.0, 4.8] +;; jem_bay3: [11.0, -6.0, 4.8] +;; jem_bay4: [11.0, -7.0, 4.8] +;; jem_bay5: [11.0, -8.0, 4.8] +;; jem_bay6: [11.0, -9.0, 4.8] +;; jem_bay7: [11.0, -9.7, 4.8] +;; +;; bogus_bays: [jem_bay0, jem_bay8] +;; berths: [berth1, berth2] +;; robots: [bumble, honey] +;; num_orders: 10 +;; +;; stereo: +;; # Meta-data about stereo survey options +;; jem_bay1_to_bay3: +;; # fplan: Name of external fplan specification of trajectory in astrobee_ops/gds/plans/ISAAC/ +;; fplan: "jem_stereo_mapping_bay1_to_bay3.fplan" +;; # base_location: Where trajectory starts and ends for planning purposes (rough location, not exact) +;; base_location: jem_bay1 +;; # bound_location: The other end of the interval covered by the trajectory, for planner collision +;; # check purposes. (Note a trajectory may fly a bit into a bay that it doesn't claim to cover. +;; # The two surveys that cover the module purposefully overlap.) +;; bound_location: jem_bay4 +;; jem_bay4_to_bay7: +;; fplan: "jem_stereo_mapping_bay4_to_bay7.fplan" +;; base_location: jem_bay7 +;; bound_location: jem_bay4 +;; +;; END CONFIG_STATIC diff --git a/astrobee/survey_manager/survey_planner/readme.md b/astrobee/survey_manager/survey_planner/readme.md new file mode 100644 index 00000000..6fd77ac8 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/readme.md @@ -0,0 +1,5 @@ +\page survey_planner Survey Planner + +Planning and scheduling of queued survey actions using Playsys2 for PDDL solutions and behavior trees for execution. + +Based on [preliminary work](https://github.com/traclabs/astrobee_task_planning_ws) by [Ana](https://github.com/ana-GT) at [Traclabs](https://traclabs.com). diff --git a/astrobee/survey_manager/survey_planner/src/ros1_lifecycle b/astrobee/survey_manager/survey_planner/src/ros1_lifecycle new file mode 160000 index 00000000..15a50352 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/src/ros1_lifecycle @@ -0,0 +1 @@ +Subproject commit 15a50352f9d522c7815ecedaa2b77b6f99ecdb31 diff --git a/astrobee/survey_manager/survey_planner/src/ros2_planning_system b/astrobee/survey_manager/survey_planner/src/ros2_planning_system new file mode 160000 index 00000000..3a988a5b --- /dev/null +++ b/astrobee/survey_manager/survey_planner/src/ros2_planning_system @@ -0,0 +1 @@ +Subproject commit 3a988a5bc14a1700a8f276e2438c47ac80e7bf8c diff --git a/astrobee/survey_manager/survey_planner/tools/plan_interpreter.py b/astrobee/survey_manager/survey_planner/tools/plan_interpreter.py new file mode 100755 index 00000000..ce98e096 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/tools/plan_interpreter.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2023, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The "ISAAC - Integrated System for Autonomous and Adaptive Caretaking +# platform" software is 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. + +""" +Provides an example of how to interpret the actions in a survey domain plan, dropping the irrelevant +arguments, mapping the positional arguments back to the names used in the dynamic config, and +looking up extra info that's not needed by the planner but is important for execution (e.g., +coordinates of named locations, fplan filenames of stereo surveys). + +For testing, you can call this from the command line on a complete sample output plan. + +For use during execution, if you are receiving one action at a time from an executor, you may +prefer to import this module and call the yaml_action_from_pddl() function directly. +""" + +import argparse +import pathlib +import re +import sys +from typing import Any, Dict, List + +import yaml + +ACTION_TYPE_OPTIONS = ("dock", "undock", "move", "panorama", "stereo") + + +# Type alias +YamlMapping = Dict[str, Any] +FloatStr = str # String representation of a floating-point value + +# POPF plan output weirdly mixes its random debugging output with the actual plan actions that it +# outputs in a standard format. (Maybe there's some way to suppress the debug info?) Anyway, this +# regex works nicely to ignore the debug info and parse the fields of the plan actions we care +# about. It might need fine-tuning if we use other planners. +PLAN_ACTION_REGEX = re.compile( + r"^(?P\d+(\.\d*)?): (?P\(.*?\))\s+\[(?P\d+(\.\d*)?)\]\s*$" +) + + +def load_yaml(yaml_path: pathlib.Path) -> YamlMapping: + """ + Return the YAML parse result for the file at `yaml_path`. + """ + with yaml_path.open(encoding="utf-8") as yaml_stream: + return yaml.safe_load(yaml_stream) + + +class PlanAction: + """ + Class representing one entry in the output plan sequence of a PDDL planner. + """ + + def __init__( + self, start_time_seconds: FloatStr, action: str, duration_seconds: FloatStr + ): + self.start_time_seconds = start_time_seconds + self.action = action + self.duration_seconds = duration_seconds + + def __repr__(self): + return f"{self.start_time_seconds}: {self.action} [{self.duration_seconds}]" + + +def yaml_action_from_pddl(action: str, static_config: YamlMapping) -> YamlMapping: + """ + Return a YamlMapping representation of `action`. This is the only place + we really need domain-specific logic. + """ + action_args = action[1:-1].split() + action_type = action_args[0] + assert ( + action_type in ACTION_TYPE_OPTIONS + ), f"Expected action type in {ACTION_TYPE_OPTIONS}, got {action_type}" + + if action_type == "dock": + robot, _from_bay, to_berth = action_args[1:] + # Can discard from_bay + return {"type": "dock", "robot": robot, "berth": to_berth} + + if action_type == "undock": + robot, _from_berth, _to_bay, _check1, _check2 = action_args[1:] + # Can discard from_berth, to_bay, check1, check2 + return {"type": "undock", "robot": robot} + + if action_type == "move": + robot, _from_bay, to_bay, _check_bay = action_args[1:] + # Can discard from_bay, check_bay. Look up coordinates for to_bay. + return { + "type": "move", + "robot": robot, + "to_name": to_bay, + "to_pos": static_config["bays"][to_bay], + } + + if action_type == "panorama": + robot, _order, location, run_name = action_args[1:] + run_number = int(run_name[-1]) + # Can discard order. Look up coordinates for location. + return { + "type": "panorama", + "robot": robot, + "location_name": location, + "location_pos": static_config["bays"][location], + "run": run_number, + } + + if action_type == "stereo": + robot, _order, base, bound, _check1, _check2, run_name = action_args[1:] + run_number = int(run_name[-1]) + # Use base and bound to look up trajectory. + traj_matches = [ + traj + for traj in static_config["stereo"].values() + if traj["base_location"] == base and traj["bound_location"] == bound + ] + assert ( + len(traj_matches) == 1 + ), f"Expected exactly 1 matching stereo trajectory with base {base} and bound {bound}, got {len(traj_matches)}" + fplan = traj_matches[0]["fplan"] + # Can discard order, base, bound, check1, check2. + return {"type": "stereo", "robot": robot, "fplan": fplan, "run": run_number} + + assert False, "Never reach this point." + return {} # Make pylint happy + + +def yaml_plan_action_from_pddl( + plan_action: PlanAction, static_config: YamlMapping +) -> YamlMapping: + """ + Return a YamlMapping representation of `plan_action`. + """ + return { + "start_time_seconds": plan_action.start_time_seconds, + "action": yaml_action_from_pddl(plan_action.action, static_config), + "duration_seconds": plan_action.duration_seconds, + } + + +def parse_plan(plan_path: pathlib.Path) -> List[PlanAction]: + """ + Return a list of PlanActions read from the PDDL plan at `plan_path`. + """ + actions = [] + with plan_path.open(encoding="utf-8") as plan_stream: + for plan_line in plan_stream: + match = PLAN_ACTION_REGEX.search(plan_line) + if match: + start_time_seconds = match.group("start_time_seconds") + action = match.group("action") + duration_seconds = match.group("duration_seconds") + + # Raise an exception if these values are not the string representation of a + # floating-point value. However, we are storing the original string representation + # rather than storing the float to avoid any weird issues with the values getting + # mangled due to floating-point precision and formatting. + float(start_time_seconds) + float(duration_seconds) + + actions.append(PlanAction(start_time_seconds, action, duration_seconds)) + return actions + + +class NoAliasDumper( + yaml.SafeDumper +): # pylint: disable=too-many-ancestors # It only has 1, so (?) + """ + Configures yaml dump output to avoid using confusing aliases. + """ + + def ignore_aliases(self, data): + return True + + +def plan_interpreter( + config_static_path: pathlib.Path, plan_path: pathlib.Path, output_path: pathlib.Path +) -> None: + """ + The main function that interprets an entire plan file. + + However, if you are receiving one action at a time from an executor, you may prefer to import + this module and call yaml_action_from_pddl() directly. + """ + config_static = load_yaml(config_static_path) + pddl_actions = parse_plan(plan_path) + yaml_actions = [ + yaml_plan_action_from_pddl(plan_action, config_static) + for plan_action in pddl_actions + ] + with output_path.open("w", encoding="utf-8") as output_stream: + yaml.dump(yaml_actions, output_stream, Dumper=NoAliasDumper, sort_keys=False) + print(f"Wrote to {output_path}", file=sys.stderr) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "--config-static", + help="Path to input static problem config YAML (module geometry, available stereo surveys, etc.)", + type=pathlib.Path, + default="jem_survey_static.yaml", + ) + parser.add_argument( + "--plan", + help="Path to input plan generated by PDDL planner (parser currently tuned for POPF idiosyncrasies)", + type=pathlib.Path, + default="sample_output_plan.txt", + ) + parser.add_argument( + "-o", + "--output", + help="Path for output converted to YAML format", + type=pathlib.Path, + default="sample_output_plan.yaml", + ) + args = parser.parse_args() + + plan_interpreter( + config_static_path=args.config_static, + plan_path=args.plan, + output_path=args.output, + ) + + +if __name__ == "__main__": + main() diff --git a/astrobee/survey_manager/survey_planner/tools/problem_generator.py b/astrobee/survey_manager/survey_planner/tools/problem_generator.py new file mode 100755 index 00000000..7b5b2b31 --- /dev/null +++ b/astrobee/survey_manager/survey_planner/tools/problem_generator.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2023, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The "ISAAC - Integrated System for Autonomous and Adaptive Caretaking +# platform" software is 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. + +""" +Generates a PDDL problem specification for the survey domain in domain_survey.pddl. Takes as input +a PDDL problem template and higher-level static and dynamic configuration specified in YAML. + +The generator takes care of error-prone repetitive tasks like asserting predicates for which +locations are different, which robots are different, asserting a need-stereo predicate for every +completed-stereo goal, making the initial location-available predicates agree with the initial robot +locations, etc. +""" + +import argparse +import itertools +import pathlib +import re +import sys +from typing import Any, Dict, Iterable, Sequence, T, Tuple + +import yaml + +GOAL_TYPE_OPTIONS = ("panorama", "stereo", "robot_at") + + +# Type alias +YamlMapping = Dict[str, Any] + +# Replace the text "{{ foo }}" in the template with the value of the foo parameter +TEMPLATE_SUBST_REGEX = re.compile(r"{{\s*([\w]+)\s*}}") + + +def load_yaml(yaml_path: pathlib.Path) -> YamlMapping: + """ + Return the YAML parse result for the file at `yaml_path`. + """ + with yaml_path.open(encoding="utf-8") as yaml_stream: + return yaml.safe_load(yaml_stream) + + +def pddl_goal_from_yaml(goal: YamlMapping, config_static: YamlMapping) -> str: + """ + Convert a YAML goal with named fields from the dynamic config into a PDDL goal predicate. + """ + goal_type = goal["type"] + assert ( + goal_type in GOAL_TYPE_OPTIONS + ), f"Expected goal type in {GOAL_TYPE_OPTIONS}, got {goal_type}" + + if goal_type == "panorama": + robot = goal["robot"] + order = goal["order"] + location = goal["location"] + run = goal["run"] + return f"(completed-panorama {robot} o{order} {location} run{run})" + + if goal_type == "stereo": + robot = goal["robot"] + order = goal["order"] + trajectory = goal["trajectory"] + run = goal["run"] + traj_info = config_static["stereo"][trajectory] + base = traj_info["base_location"] + bound = traj_info["bound_location"] + return f"(completed-stereo {robot} o{order} {base} {bound} run{run})" + + if goal_type == "robot_at": + robot = goal["robot"] + location = goal["location"] + return f"(robot-at {robot} {location})" + + assert False, "Never reach this point" + return {} # Make pylint happy + + +def indent_lines(lines: Sequence[str], indent: int) -> str: + """ + Return `lines` joined with carriage returns, prepending `indent` spaces to each line after the + first. + """ + sep = "\n" + (" " * indent) + return sep.join(lines) + + +def pairwise(elts: Iterable[T]) -> Iterable[Tuple[T, T]]: + """ + Returns consecutive pairs drawn from `elts`. Back-port itertools.pairwise() to earlier Python + versions. + """ + a, b = itertools.tee(elts) + next(b, None) + return zip(a, b) + + +def both_ways(elts: Iterable[Tuple[T, T]]) -> Iterable[Tuple[T, T]]: + """ + Given `elts` an iterable of 2-tuples, return a new iterable that includes each 2-tuple twice, + once in its original order and once reversed. + """ + for a, b in elts: + yield a, b + yield b, a + + +def distinct_pairs(elts: Iterable[T]) -> Iterable[Tuple[T, T]]: + """ + Return distinct pairs of items drawn from `elts`. + """ + for a, b in itertools.product(elts, repeat=2): + if a != b: + yield a, b + + +class TemplateFiller: + """ + Class for substituting parameters into a template. + """ + + def __init__(self, params): + self.params = params + + def __call__(self, match: re.match) -> str: + param = match.group(1) + if param not in self.params: + raise KeyError( + "Expected template param in {self.params.keys()}, got {{{{ {param} }}}}" + ) + return self.params[param] + + +def comment_for_pddl(text: str) -> str: + """ + Return the result of commenting `text` using PDDL (Lisp-like) comment syntax. + """ + return ";; " + text.replace("\n", "\n;; ") + + +def problem_generator( + problem_template_path: pathlib.Path, + config_dynamic_path: pathlib.Path, + config_static_path: pathlib.Path, + output_path: pathlib.Path, +) -> None: + """ + The main function that generates the problem. + """ + problem_template = problem_template_path.read_text() + config_dynamic = load_yaml(config_dynamic_path) + config_static = load_yaml(config_static_path) + params = {} + + params["header"] = ( + ";; Auto-generated by problem_generator.py. Do not edit!\n" + f";; Problem template: {problem_template_path}\n" + f";; Dynamic config: {config_dynamic_path}\n" + f";; Static config: {config_static_path}\n" + ) + + bays = list(config_static["bays"].keys()) + bogus_bays = config_static["bogus_bays"] + all_bays = sorted(bays + bogus_bays) + params["bays"] = " ".join(all_bays) + + berths = config_static["berths"] + params["berths"] = " ".join(berths) + + robots = config_static["robots"] + params["robots"] = " ".join(robots) + + num_orders = config_static["num_orders"] + params["orders"] = " ".join([f"o{i}" for i in range(num_orders)]) + + yaml_goals = config_dynamic["goals"] + pddl_goals = [pddl_goal_from_yaml(goal, config_static) for goal in yaml_goals] + params["goals"] = indent_lines(pddl_goals, 12) + + move_connected_lines = [ + f"(move-connected {a} {b})" for a, b in both_ways(pairwise(all_bays)) + ] + params["move_connected_predicates"] = indent_lines(move_connected_lines, 8) + + location_real_lines = [f"(location-real {bay})" for bay in bays] + params["location_real_predicates"] = indent_lines(location_real_lines, 8) + + candidates = (("jem_bay7", "berth1"), ("jem_bay7", "berth2")) + dock_connected_lines = [ + f"(dock-connected {bay} {berth})" + for bay, berth in candidates + if bay in bays and berth in berths + ] + params["dock_connected_predicates"] = indent_lines(dock_connected_lines, 8) + + robots_different_lines = [ + f"(robots-different {a} {b})" for a, b in distinct_pairs(robots) + ] + params["robots_different_predicates"] = indent_lines(robots_different_lines, 8) + + locations_different_lines = [ + f"(locations-different {a} {b})" for a, b in distinct_pairs(all_bays) + ] + params["locations_different_predicates"] = indent_lines( + locations_different_lines, 8 + ) + + robot_available_lines = [f"(robot-available {robot})" for robot in robots] + params["robot_available_predicates"] = indent_lines(robot_available_lines, 8) + + init = config_dynamic["init"] + robot_at_lines = [ + f"(robot-at {robot} {init[robot]['location']})" for robot in robots + ] + params["robot_at_predicates"] = indent_lines(robot_at_lines, 8) + + all_locations = all_bays + berths + occupied_locations = [init[robot]["location"] for robot in robots] + available_locations = sorted(set(all_locations).difference(occupied_locations)) + location_available_lines = [ + f"(location-available {location})" for location in available_locations + ] + params["location_available_predicates"] = indent_lines(location_available_lines, 8) + + need_stereo_lines = [ + goal.replace("completed-stereo", "need-stereo") + for goal in pddl_goals + if "completed-stereo" in goal + ] + params["need_stereo_predicates"] = indent_lines(need_stereo_lines, 8) + + order_identity_lines = [f"(= (order-identity o{i}) {i})" for i in range(num_orders)] + params["order_identity_fluents"] = indent_lines(order_identity_lines, 8) + + robot_order_lines = [f"(= (robot-order {robot}) -1)" for robot in robots] + params["robot_order_fluents"] = indent_lines(robot_order_lines, 8) + + params["config_dynamic"] = comment_for_pddl(config_dynamic_path.read_text()) + params["config_static"] = comment_for_pddl(config_static_path.read_text()) + + # print(yaml.safe_dump(params, indent=4, sort_keys=False)) + filled_template = TEMPLATE_SUBST_REGEX.sub(TemplateFiller(params), problem_template) + output_path.write_text(filled_template) + print(f"Wrote to {output_path}", file=sys.stderr) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "--problem-template", + help="Path to input PDDL problem template", + type=pathlib.Path, + default="jem_survey_template.pddl", + ) + parser.add_argument( + "--config-dynamic", + help="Path to input dynamic problem config YAML (goals, initial state)", + type=pathlib.Path, + default="jem_survey_dynamic.yaml", + ) + parser.add_argument( + "--config-static", + help="Path to input static problem config YAML (module geometry, available stereo surveys, etc.)", + type=pathlib.Path, + default="jem_survey_static.yaml", + ) + parser.add_argument( + "-o", + "--output", + help="Path for output PDDL problem", + type=pathlib.Path, + default="problem_jem_survey.pddl", + ) + args = parser.parse_args() + + problem_generator( + problem_template_path=args.problem_template, + config_dynamic_path=args.config_dynamic, + config_static_path=args.config_static, + output_path=args.output, + ) + + +if __name__ == "__main__": + main() diff --git a/isaac/Subsystems.md b/isaac/Subsystems.md index bff1f708..ddd1e7a8 100644 --- a/isaac/Subsystems.md +++ b/isaac/Subsystems.md @@ -13,4 +13,6 @@ The ISAAC Repo contains the following Modules: \subpage astrobee : High-level actions that support complex maneuvers. +\subpage survey : Semi-autonomous survey tasking of Astrobees. + \subpage shared : Shared tools common to the entire ISAAC repo. diff --git a/scripts/docker/astrobee_msgs.Dockerfile b/scripts/docker/astrobee_msgs.Dockerfile index 1ac4e76c..1a8e4716 100644 --- a/scripts/docker/astrobee_msgs.Dockerfile +++ b/scripts/docker/astrobee_msgs.Dockerfile @@ -19,11 +19,11 @@ # This will set up an Astrobee docker container using the non-NASA install instructions. # You must set the docker context to be the repository root directory -ARG UBUNTU_VERSION=16.04 +ARG UBUNTU_VERSION=20.04 FROM ubuntu:${UBUNTU_VERSION} -ARG ROS_VERSION=kinetic -ARG PYTHON="" +ARG ROS_VERSION=noetic +ARG PYTHON=3 # try to suppress certain warnings during apt-get calls ARG DEBIAN_FRONTEND=noninteractive diff --git a/scripts/docker/isaac.Dockerfile b/scripts/docker/isaac.Dockerfile index 18fb3cd3..4ee67c98 100644 --- a/scripts/docker/isaac.Dockerfile +++ b/scripts/docker/isaac.Dockerfile @@ -19,12 +19,12 @@ # This will set up an Astrobee docker container using the non-NASA install instructions. # You must set the docker context to be the repository root directory -ARG UBUNTU_VERSION=16.04 +ARG UBUNTU_VERSION=20.04 ARG REMOTE=isaac FROM ${REMOTE}/isaac:latest-astrobee-ubuntu${UBUNTU_VERSION} -ARG ROS_VERSION=kinetic -ARG PYTHON="" +ARG ROS_VERSION=noetic +ARG PYTHON=3 # suppress detached head warnings later RUN git config --global advice.detachedHead false diff --git a/scripts/docker/isaac_astrobee.Dockerfile b/scripts/docker/isaac_astrobee.Dockerfile index 269c85ca..12afb06d 100644 --- a/scripts/docker/isaac_astrobee.Dockerfile +++ b/scripts/docker/isaac_astrobee.Dockerfile @@ -19,13 +19,13 @@ # This will set up an Astrobee docker container using the non-NASA install instructions. # You must set the docker context to be the repository root directory -ARG UBUNTU_VERSION=16.04 +ARG UBUNTU_VERSION=20.04 ARG REMOTE=astrobee FROM ${REMOTE}/astrobee:latest-ubuntu${UBUNTU_VERSION} # Already inherited from astrobee:base-latest-ubuntu... -ARG ROS_VERSION=kinetic -ARG PYTHON="" +ARG ROS_VERSION=noetic +ARG PYTHON=3 RUN apt-get update && apt-get install -y \ libmnl-dev \ diff --git a/scripts/docker/isaac_msgs.Dockerfile b/scripts/docker/isaac_msgs.Dockerfile index 27190252..280c5ae8 100644 --- a/scripts/docker/isaac_msgs.Dockerfile +++ b/scripts/docker/isaac_msgs.Dockerfile @@ -19,12 +19,12 @@ # This will set up an Astrobee docker container using the non-NASA install instructions. # You must set the docker context to be the repository root directory -ARG UBUNTU_VERSION=16.04 +ARG UBUNTU_VERSION=20.04 ARG REMOTE=isaac FROM ${REMOTE}/isaac:astrobee-msgs-ubuntu${UBUNTU_VERSION} -ARG ROS_VERSION=kinetic -ARG PYTHON="" +ARG ROS_VERSION=noetic +ARG PYTHON=3 # Copy over the isaac_msgs COPY communications/isaac_msgs /src/msgs/src/communications/ diff --git a/scripts/setup/packages_focal.lst b/scripts/setup/packages_focal.lst index 87954901..e1c70e58 100644 --- a/scripts/setup/packages_focal.lst +++ b/scripts/setup/packages_focal.lst @@ -2,7 +2,9 @@ doxygen python3-catkin-tools python3-osrf-pycommon python3-rosdep +libreadline-dev ros-noetic-eigen-conversions ros-noetic-pcl-ros +ros-noetic-behaviortree-cpp libmnl-dev libproj-dev