diff --git a/.catkin_tools/CATKIN_IGNORE b/.catkin_tools/CATKIN_IGNORE deleted file mode 100644 index e69de29b..00000000 diff --git a/.catkin_tools/README b/.catkin_tools/README deleted file mode 100644 index 4706f475..00000000 --- a/.catkin_tools/README +++ /dev/null @@ -1,13 +0,0 @@ -# Catkin Tools Metadata - -This directory was generated by catkin_tools and it contains persistent -configuration information used by the `catkin` command and its sub-commands. - -Each subdirectory of the `profiles` directory contains a set of persistent -configuration options for separate profiles. The default profile is called -`default`. If another profile is desired, it can be described in the -`profiles.yaml` file in this directory. - -Please see the catkin_tools documentation before editing any files in this -directory. Most actions can be performed with the `catkin` command-line -program. diff --git a/.catkin_tools/VERSION b/.catkin_tools/VERSION deleted file mode 100644 index 7ceb0404..00000000 --- a/.catkin_tools/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.6.1 \ No newline at end of file diff --git a/.catkin_tools/profiles/cross/config.yaml b/.catkin_tools/profiles/cross/config.yaml deleted file mode 100644 index 5996f24e..00000000 --- a/.catkin_tools/profiles/cross/config.yaml +++ /dev/null @@ -1,19 +0,0 @@ -blacklist: [] -build_space: build -catkin_make_args: [] -cmake_args: -- -DCMAKE_TOOLCHAIN_FILE=$ISAAC_WS/src/scripts/build/astrobee_cross.cmake -- -DARMHF_CHROOT_DIR=$ISAAC_WS/arm_cross/rootfs -devel_layout: linked -devel_space: devel -extend_path: $ISAAC_WS/arm_cross/rootfs/opt/astrobee -install: true -install_space: install -isolate_install: false -jobs_args: [] -log_space: logs -make_args: [] -source_space: src -use_env_cache: false -use_internal_make_jobserver: true -whitelist: [] diff --git a/.catkin_tools/profiles/default/config.yaml b/.catkin_tools/profiles/default/config.yaml deleted file mode 100644 index 4b2ebed9..00000000 --- a/.catkin_tools/profiles/default/config.yaml +++ /dev/null @@ -1,17 +0,0 @@ -blacklist: [] -build_space: build -catkin_make_args: [] -cmake_args: [] -devel_layout: linked -devel_space: devel -extend_path: null -install: false -install_space: install -isolate_install: false -jobs_args: [] -log_space: logs -make_args: [] -source_space: src -use_env_cache: false -use_internal_make_jobserver: true -whitelist: [] diff --git a/.github/workflows/apk.yaml b/.github/workflows/apk.yaml index 40100b33..ea3e5768 100644 --- a/.github/workflows/apk.yaml +++ b/.github/workflows/apk.yaml @@ -2,21 +2,21 @@ name: Compile APK -on: ['push', 'pull_request'] +on: ['push', 'pull_request', 'workflow_dispatch'] jobs: install_dependencies: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ @@ -36,36 +36,9 @@ jobs: --build-arg PYTHON='' -t isaac/isaac:msgs-ubuntu16.04 - - name: Build image isaac/isaac:latest-msgs-jar-ubuntu16.04 - run: docker build isaac -f isaac/scripts/docker/build_msgs_jar.Dockerfile + - name: Build image isaac/isaac:latest-akp-ubuntu16.04 + run: docker build isaac -f isaac/scripts/docker/build_apk.Dockerfile --build-arg UBUNTU_VERSION=16.04 --build-arg ROS_VERSION=kinetic --build-arg PYTHON='' - -t isaac/isaac:latest-msgs-jar-ubuntu16.04 - - - name: Copy jar files - run: | - docker cp $(docker create --rm isaac/isaac:latest-msgs-jar-ubuntu16.04):/src/msgs/devel/share/maven/ . - rm isaac/apks/isaac_gs_ros_bridge/app/libs/*msgs* - find maven -name *.jar -print0 | xargs -0 cp -t isaac/apks/isaac_gs_ros_bridge/app/libs - - - name: Install APK dependencies - run: | - sudo apt-get install -y libc6-dev-i386 lib32z1 openjdk-8-jdk - mkdir $HOME/android-sdk - cd $HOME/android-sdk - wget https://dl.google.com/android/repository/tools_r25.2.3-linux.zip - java -version - unzip tools_r25.2.3-linux.zip - tools/bin/sdkmanager --update - yes | tools/bin/sdkmanager "platforms;android-25" "build-tools;25.0.2" "extras;google;m2repository" "extras;android;m2repository" - wget https://dl.google.com/android/repository/android-ndk-r22b-linux-x86_64.zip - unzip android-ndk-r22b-linux-x86_64.zip - mv android-ndk-r22b ndk-bundle - cd ~/android-sdk/ndk-bundle/toolchains - ln -s aarch64-linux-android-4.9 mips64el-linux-android - ln -s arm-linux-androideabi-4.9 mipsel-linux-android - - name: Build APK - run: | - cd isaac/apks/isaac_gs_ros_bridge - ANDROID_HOME=$HOME/android-sdk ANDROID_NDK_HOME=$HOME/android-sdk/ndk-bundle ./gradlew build + -t isaac/isaac:latest-apk-ubuntu16.04 diff --git a/.github/workflows/ci_pr.yml b/.github/workflows/ci_pr.yml index ebc9ada8..1cf1a924 100644 --- a/.github/workflows/ci_pr.yml +++ b/.github/workflows/ci_pr.yml @@ -3,23 +3,24 @@ name: Build and Test CI for Pull Requests on: pull_request: branches: [ 'master', 'develop' ] + workflow_dispatch: jobs: build-xenial: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ @@ -53,18 +54,18 @@ jobs: -t isaac/isaac:msgs-ubuntu16.04 build-bionic: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ @@ -104,13 +105,13 @@ jobs: steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ @@ -146,4 +147,4 @@ jobs: - name: Build analyst image isaac/isaac:msgs-ubuntu20.04 run: docker build isaac -f isaac/scripts/docker/analyst.Dockerfile - -t isaac/isaac_analyst_notebook:latest \ No newline at end of file + -t isaac/isaac_analyst_notebook:latest diff --git a/.github/workflows/ci_push.yml b/.github/workflows/ci_push.yml index b9914462..fc92a580 100644 --- a/.github/workflows/ci_push.yml +++ b/.github/workflows/ci_push.yml @@ -3,23 +3,25 @@ name: Build, test and push packages CI on: push: branches: [ 'develop' ] + workflow_dispatch: + branches: [ 'develop' ] jobs: build-xenial: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ @@ -67,18 +69,18 @@ jobs: build-bionic: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ @@ -131,13 +133,13 @@ jobs: steps: - name: Checkout Astrobee - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: nasa/astrobee path: astrobee/ - name: Checkout ISAAC - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive path: isaac/ diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml index d47ab4a8..3db65e46 100644 --- a/.github/workflows/ci_release.yml +++ b/.github/workflows/ci_release.yml @@ -3,15 +3,17 @@ name: Build, test and push packages CI on: push: branches: [ 'master' ] + workflow_dispatch: + branches: [ 'master' ] jobs: build-xenial: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Checkout submodule run: git submodule update --init --depth 1 isaac_msgs @@ -45,10 +47,10 @@ jobs: build-bionic: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Checkout submodule run: git submodule update --init --depth 1 isaac_msgs @@ -85,7 +87,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Checkout submodule run: git submodule update --init --depth 1 isaac_msgs diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 74bfac43..1e0f41b7 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -5,18 +5,20 @@ name: Build and Push Documentation to gh-pages Branch on: push: branches: ['develop'] + workflow_dispatch: + branches: ['develop'] jobs: build_and_push_docs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: repo/ - name: Checkout gh-pages - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: docs/ ref: gh-pages diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 96063314..d779f6d1 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -2,14 +2,14 @@ name: Check for lint errors -on: ['push', 'pull_request'] +on: ['push', 'pull_request', 'workflow_dispatch'] jobs: lint_check_cpp: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check repo for lint errors run: ./scripts/git/cpplint_repo.py . @@ -17,11 +17,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install linters run: | - pip install black - pip install isort + pip install click==8.0.1 black==22.1.0 isort==5.10.1 - name: Run black run: | diff --git a/.gitignore b/.gitignore index 46089f6b..448cd514 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # ROS *.bag +.catkin_tools/* # Fortran module files *.mod @@ -16,6 +17,7 @@ *.app # Emacs temporary +*~ # ViM swap files *.swp @@ -48,9 +50,15 @@ tags doc/html doc/latex +# Pytorch models +analyst/workspace/*.pt + # Jupyter Notebooks analyst/workspace/.ipython analyst/workspace/.jupyter analyst/workspace/.local analyst/workspace/.ipynb_checkpoints -analyst/workspace/.cache \ No newline at end of file +analyst/workspace/.cache + +# vscode +isaac.code-workspace \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 77d515f9..b94bfb0e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "isaac_msgs"] - path = isaac_msgs + path = communications/isaac_msgs url = https://github.com/nasa/isaac_msgs.git diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..46f14eaf --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,18 @@ +[settings] + +# The src_paths setting should be a comma-separated list of all the +# folders in the repo that contain *.py files. Python files found in +# these folders count as "first party" so they should be in a separate +# group from the "third party" imports. If src_paths is not specified, +# isort will treat only the files it's asked to check as first +# party. Therefore, if we specify src_paths broadly in this way, isort +# behavior should be more repeatable between (1) manually running isort +# on a single file in your local dev machine, vs. (2) running the git +# pre-commit hook locally on your dev machine, which historically ran +# isort only on the files that changed since the last commit, or (3) +# running the CI workflow, which always runs isort on all files. If +# src_paths needs to be updated, like if *.py files are added to a new +# folder, you can auto-update it by running +# scripts/git/configure_isort_paths.sh. + +src_paths = analyst/workspace/scripts,anomaly/image/scripts,astrobee/behaviors/inspection/scripts,astrobee/simulation/acoustics_cam/src,dense_map/geometry_mapper/tools,isaac_msgs/isaac_msgs/test,pano/pano_stitch/scripts,pano/pano_view/scripts,scripts/git diff --git a/INSTALL.md b/INSTALL.md index b71c1f2b..0c502a87 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -69,43 +69,81 @@ Source your astrobee build environment, for example as: source $ASTROBEE_WS/devel/setup.bash -Build with catkin build. +The configure script prepares your build directory for compiling the code. Note +that `configure.sh` is simply a wrapper around CMake that provides an easy way +of turning on and off options. To see which options are supported, simply run +`configure.sh -h`. + + pushd $ASTROBEE_WS + ./src/scripts/configure.sh -l + source ~/.bashrc + popd - cd $ISAAC_WS - catkin init - catkin build - source devel/setup.bash +The configure script modifies your ``.bashrc`` to source ``setup.bash`` for +the current ROS distribution and to set CMAKE_PREFIX_PATH. It is suggested +to examine it and see if all changes were made correctly. + +If you want to explicitly specify the workspace and/or install directories, use +instead: -The command 'source devel/setup.bash' is very important to make sure that everything is correctly linked! Everytime some major change in package is made, a full build should be done as: + ./scripts/configure.sh -l -p $INSTALL_PATH -w $WORKSPACE_PATH - catkin clean +*Note: If a workspace is specified but not an explicit install distectory, +install location will be $WORKSPACE_PATH/install.* + +To build, run `catkin build` in the `$WORKSPACE_PATH`. Note that depending on your host +machine, this might take in the order of tens of minutes to complete the first +time round. Future builds will be faster, as only changes to the code are +rebuilt, and not the entire code base. + + pushd $ASTROBEE_WS catkin build - source devel/setup.bash + popd If you are working in simulation only, then you're all done! The next steps are only for running ISAAC onboard Astrobee. -Cross-compiling isaac + +Cross-compiling isaac (NASA only) --------- +To cross-compile ISAAC, one must first cross compile the astobee code using the NASA_INSTALL instructions. Note that `ASTROBEE_WS` must be defined!!! -To cross-compile ISAAC, one must first cross compile the astobee code using the NASA_INSTALL instructions. Note that ASTROBEE_WS must be defined. -A new catkin profile should be made to retain the configurations and easily switch between normal build. - - catkin profile add cross - catkin profile set cross - catkin config --extend $ASTROBEE_WS/armhf/opt/astrobee \ - --build-space armhf/build \ - --install-space armhf/opt/isaac \ - --devel-space armhf/devel \ - --log-space armhf/logs \ - --whitelist isaac_astrobee_description isaac_util isaac_msgs inspection cargo isaac_hw_msgs wifi isaac gs_action_helper \ - --install \ - --cmake-args -DCMAKE_TOOLCHAIN_FILE=$ISAAC_WS/src/scripts/build/isaac_cross.cmake \ - -DARMHF_CHROOT_DIR=$ARMHF_CHROOT_DIR - -Build ISAAC debian +Cross compiling for the robot follows the same process, except the configure +script takes a `-a` flag instead of `-l`. + + pushd $ISAAC_WS + ./src/scripts/configure.sh -a + popd + +Or with explicit build and install paths: + + ./scripts/configure.sh -a -p $INSTALL_PATH -w $WORKSPACE_PATH + +*Warning: `$INSTALL_PATH` and `$WORKSPACE_PATH` used for cross compiling HAVE to be +different than the paths for native build! See above for the default values +for these.* + + Once the code has been built, it also installs the code to +a singular location. CMake remembers what `$INSTALL_PATH` you specified, and +will copy all products into this directory. + +Install the code on the robot (NASA only) +--------- + +Once the installation has completed, copy the install directory to the robot. +This script assumes that you are connected to the Astrobee network, as it uses +rsync to copy the install directory to `~/armhf` on the two processors. It +takes the robot name as an argument. Here we use `p4d`. + + pushd $ISAAC_WS + ./src/scripts/install_to_astrobee.sh $INSTALL_PATH p4d + popd + +Here, p4d is the name of the robot, which may be different in your case. + +Build ISAAC debian (NASA only) --------- To build a debian you must first confirm that cross-compiling is functional. Once it is: diff --git a/RELEASE.md b/RELEASE.md index 112cfe28..be09dc33 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,12 @@ # Releases +## Release 0.2.6 + + * Upgrades on inspection tool + * Auto sci cam restart + * Ubuntu 20 debian support + * Multiple other fixes + ## Release 0.2.5 * Add sci cam plugin to isaac repo diff --git a/analyst/readme.md b/analyst/readme.md index 014bdfa5..5aeff4b7 100644 --- a/analyst/readme.md +++ b/analyst/readme.md @@ -1,4 +1,59 @@ \page analyst Analyst Notebook -copied into the jupyter notebook home folder, are scripts that are made by the analyst and will be saved, even after the docker is shutdown. \ No newline at end of file +# Starting the Analyst Notebook + +**The jupyter notebooks will be able to access data that is in the `$HOME/data` and `$HOME/data/bags`, therefore, make sure all the relevant bag files are there** + + +For the Analyst notebook to be functional, it needs to start side-by-side with the database and the IUI (ISAAC user interface). +To do so, the recommended method is to use the remote docker images, as: + + $ISAAC_SRC/scripts/docker/run.sh --no-mast -analyst --no-sim -remote + +The ISAAC UI is hosted in: http://localhost:8080 +The ArangoDB database is hosted in: http://localhost:8529 +The Analyst Notebook is hosted in: http://localhost:8888/lab?token=isaac + +# Intro Tutorials + +Please follow all the tutorial to familiarize yourself with the available functions and to detect if something is not working properly. + +## 1) Import Bagfile data to database (optional if using remote database) + +Open the tutorial [here](http://localhost:8888/lab/tree/1_import_bagfiles.ipynb). + +This tutorial covers how to upload bag files to a local database. Be aware that uploading large bag files might take a long time. If possible select only the time intervals/topic names that are required for analysis to speed up the process. + + +## 2) Read data from the Database + +Open the tutorial [here](http://localhost:8888/lab/tree/2_read_database.ipynb). + +This tutorial covers how to display data uploaded to the database. It contains some examples of the most common data type / topics. +You can filter the data that gets collected from the database using queries. + +## 3) Export results to ISAAC user interface + +Open the tutorial [here](http://localhost:8888/lab/tree/3_export_result_to_iui.ipynb). + +This tutorial covers the available methods to visualize data in the ISAAC user interface (IUI). + +Open the IUI 3D viewer [here](http://localhost:8080). + + +# Case study Tutorials + +## Collecting simulation data to train a CNN + validate with ISS data + +Open the tutorial \href[here](http://localhost:8888/lab/tree/build_CNN_with_pytorch.ipynb). + +Here, we use simulation tools to automatically build a train and test dataset. The simulation dataset builder uses arguments as target position model positions and gaussian noise to build. +Using the simulated data, we use pytorch to train the classifier of a previously trained CNN. We optimize the CNN using the train dataset, and use the test dataset to decide which iteration of the optimization to keep. +With the trained CNN we can run new colledted data through it, namely real image captured data. + + +## Building a volumetric map of WiFi signal intensity + analyse model + visualize data + +Open the tutorial + diff --git a/analyst/workspace/1_import_bagfiles.ipynb b/analyst/workspace/1_import_bagfiles.ipynb index 20581b1b..7d2ef362 100644 --- a/analyst/workspace/1_import_bagfiles.ipynb +++ b/analyst/workspace/1_import_bagfiles.ipynb @@ -29,12 +29,8 @@ "# Open the isaac database / create it if it does not exist\n", "if not conn.hasDatabase(\"isaac\"):\n", " conn.createDatabase(name=\"isaac\")\n", - "db = conn[\"isaac\"]\n", - "\n", - "# Create a collection\n", - "if not db.hasCollection(\"analyst\"):\n", - " db.createCollection(name=\"analyst\")\n", " \n", + "db = conn[\"isaac\"] \n", "print(\"Connected to database!\")" ] }, @@ -42,7 +38,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The next step is to select the bagfile to import" + "The next step is to select the bagfiles directory to import.\n", + "This script will go through all the bags in the folder and import the topics to the database." ] }, { @@ -52,27 +49,10 @@ "outputs": [], "source": [ "from scripts.load_bag_database import LoadBagDatabase\n", - "\n", - "\n", "path=\"/home/analyst/data/bags/\"\n", - "\n", - "LoadBagDatabase(db, path, \"/gnc/ekf\")" + "LoadBagDatabase(db, path)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, diff --git a/analyst/workspace/2_read_database.ipynb b/analyst/workspace/2_read_database.ipynb new file mode 100644 index 00000000..3ebfdf2d --- /dev/null +++ b/analyst/workspace/2_read_database.ipynb @@ -0,0 +1,285 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5636fde2-c963-48b5-a883-a7d520a6084e", + "metadata": {}, + "source": [ + "# Read Various Data Types from Database" + ] + }, + { + "cell_type": "markdown", + "id": "718aa167-569d-4212-99ab-b20b548c39f8", + "metadata": {}, + "source": [ + "Again, make sure you connect to the isaac database" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "eb0eefdc-75b9-4d5e-a9c2-d75ea94877b1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to database\n" + ] + } + ], + "source": [ + "from pyArango.connection import *\n", + "\n", + "# Connect to the database\n", + "conn = Connection(arangoURL=\"http://iui_arangodb:8529\", username=\"root\", password=\"isaac\")\n", + "\n", + "# Open the isaac database / create it if it does not exist\n", + "if not conn.hasDatabase(\"isaac\"):\n", + " print(\"There is no isaac database, did you load it?\")\n", + "else:\n", + " db = conn[\"isaac\"]\n", + " print(\"Connected to database\")" + ] + }, + { + "cell_type": "markdown", + "id": "7dffefd4-3341-4a28-84a4-10eb437226a2", + "metadata": {}, + "source": [ + "Check which collections are available to read from:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0fe8048-f2b9-46fc-a072-5bbe35222e0b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "cf2e9dd1-6fab-487b-92f3-6a7bdd74a276", + "metadata": {}, + "source": [ + "Read 1D Data\n", + "================\n", + "\n", + "To read signal data from the database you can filter by timestamp:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fa0d4200-9c59-4b99-807a-f64f28dfc483", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "# Edit timestamps timestamp\n", + "start_time = 0\n", + "end_time = 99999999999\n", + "ros_topic = \"/gnc/ekf\"\n", + "\n", + "aql = \"\"\n", + "if start_time is not None and end_time is not None:\n", + " ros_topic = ros_topic.replace(\"/\", \"_\")[1:]\n", + "\n", + " aql = \"FOR doc IN \" + ros_topic + \"\\n\"\\\n", + " + \"\\tFILTER doc.message.header.stamp.secs >= \" + str(start_time) + \\\n", + " \" AND doc.message.header.stamp.secs <= \" + str(end_time) + \"\\n\" \\\n", + " + \"\\tRETURN doc.message\";\n", + "\n", + "result = list(db.AQLQuery(aql, rawResults = True))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9288a825-64f6-4177-9f65-9bff1ea74210", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "49afe196-726e-4f50-8e29-85eb1d9c76b3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "\n", + "# Plot the robot's position during the time interval\n", + "time = [msg.get(\"header\").get(\"stamp\").get(\"secs\") - result[0].get(\"header\").get(\"stamp\").get(\"secs\") for msg in result] \n", + "pos_x = [msg.get(\"pose\").get(\"position\").get(\"x\") for msg in result]\n", + "pos_y = [msg.get(\"pose\").get(\"position\").get(\"y\") for msg in result]\n", + "pos_z = [msg.get(\"pose\").get(\"position\").get(\"z\") for msg in result]\n", + "\n", + "# plt.plot(time,pos_x)\n", + "plt.plot(pos_y[:30000])\n", + "# plt.plot(time,pos_z)" + ] + }, + { + "cell_type": "markdown", + "id": "1f9b86af-6887-4d36-9c99-d4f879648112", + "metadata": { + "tags": [] + }, + "source": [ + "Read 2D Data\n", + "===========\n", + "To read data like images:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6a5766d7-9888-4f64-8dae-6bc1d21f0720", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "# Edit timestamps timestamp\n", + "start_time = 1616777590\n", + "end_time = 1616777600\n", + "ros_topic = \"/mgt/img_sampler/nav_cam/image_record\"\n", + "\n", + "\n", + "aql = \"\"\n", + "if start_time is not None and end_time is not None:\n", + " ros_topic = ros_topic.replace(\"/\", \"_\")[1:]\n", + "\n", + " aql = \"FOR doc IN \" + ros_topic + \"\\n\"\\\n", + " + \"\\tFILTER doc.message.header.stamp.secs >= \" + str(start_time) + \\\n", + " \" AND doc.message.header.stamp.secs <= \" + str(end_time) + \"\\n\" \\\n", + " + \"\\tRETURN doc.message\";\n", + "\n", + "result = db.AQLQuery(aql, rawResults = True)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e444acbd-da01-4751-be2d-8d44f9c3809f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b505d558-fb56-45a0-a32a-45e1175b1f14", + "metadata": {}, + "outputs": [], + "source": [ + "# Convert image to ros message\n", + "from sensor_msgs.msg import Image\n", + "import genpy\n", + "\n", + "expect = Image()\n", + "expect_str = result[0]\n", + "genpy.message.fill_message_args(expect, expect_str)\n", + "expect.data = bytes(expect.data)\n", + "\n", + "\n", + "# View image\n", + "import cv2\n", + "import numpy as np\n", + "from cv_bridge import CvBridge, CvBridgeError\n", + "\n", + "bridge = CvBridge()\n", + "cv_image = bridge.imgmsg_to_cv2(expect, \"mono8\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "3c5a278f-a3da-43d3-b926-7d66195f8810", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "\n", + "plt.imshow(cv_image, cmap='gray', vmin=0, vmax=255)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38daaa5b-cf01-48cd-b655-097bcc9e636c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/analyst/workspace/2_read_from_database.ipynb b/analyst/workspace/2_read_from_database.ipynb deleted file mode 100644 index b00161c5..00000000 --- a/analyst/workspace/2_read_from_database.ipynb +++ /dev/null @@ -1,49 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "5636fde2-c963-48b5-a883-a7d520a6084e", - "metadata": {}, - "source": [ - "# Read Various Data Types from Database" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "23334b94-b585-4319-a75b-eeacece22852", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9288a825-64f6-4177-9f65-9bff1ea74210", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/analyst/workspace/3_export_result_to_iui.ipynb b/analyst/workspace/3_export_result_to_iui.ipynb new file mode 100644 index 00000000..8d0fe143 --- /dev/null +++ b/analyst/workspace/3_export_result_to_iui.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0ce5e832-30dd-41bd-83da-a1d5c6bd3beb", + "metadata": {}, + "source": [ + "Visualize data in the ISAAC user interface\n", + "============================================" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6cded5c2-a9a7-41ce-9021-83c5aa6a3784", + "metadata": {}, + "outputs": [], + "source": [ + "In order to visualize the generated 3D data on the ISAAC user interface (IUI)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7eacaafc-7495-46c1-b4bd-a6f3a2121d64", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[INFO] [1649703584.243937]: hello world 1649703584.2438097\n", + "[INFO] [1649703584.344204]: hello world 1649703584.3440566\n", + "[INFO] [1649703584.447240]: hello world 1649703584.4471157\n", + "[INFO] [1649703584.544407]: hello world 1649703584.5442688\n", + "[INFO] [1649703584.644114]: hello world 1649703584.643913\n", + "[INFO] [1649703584.744096]: hello world 1649703584.7439532\n", + "[INFO] [1649703584.843938]: hello world 1649703584.843814\n", + "[INFO] [1649703584.944077]: hello world 1649703584.943947\n", + "[INFO] [1649703585.043972]: hello world 1649703585.0438414\n", + "[INFO] [1649703585.144743]: hello world 1649703585.144618\n", + "[INFO] [1649703585.244484]: hello world 1649703585.2443454\n", + "[INFO] [1649703585.344676]: hello world 1649703585.3445451\n", + "[INFO] [1649703585.444231]: hello world 1649703585.4440665\n", + "[INFO] [1649703585.543857]: hello world 1649703585.5437095\n", + "[INFO] [1649703585.643992]: hello world 1649703585.6438625\n", + "[INFO] [1649703585.744792]: hello world 1649703585.7446165\n", + "[INFO] [1649703585.844507]: hello world 1649703585.8443663\n", + "[INFO] [1649703585.944038]: hello world 1649703585.9438536\n", + "[INFO] [1649703586.043989]: hello world 1649703586.0438454\n", + "[INFO] [1649703586.144968]: hello world 1649703586.1448412\n", + "[INFO] [1649703586.244450]: hello world 1649703586.2442749\n", + "[INFO] [1649703586.343907]: hello world 1649703586.3437853\n", + "[INFO] [1649703586.443928]: hello world 1649703586.443807\n", + "[INFO] [1649703586.544345]: hello world 1649703586.544222\n", + "[INFO] [1649703586.643933]: hello world 1649703586.6438162\n", + "[INFO] [1649703586.744846]: hello world 1649703586.7447166\n", + "[INFO] [1649703586.844702]: hello world 1649703586.8445654\n", + "[INFO] [1649703586.944558]: hello world 1649703586.9444067\n", + "[INFO] [1649703587.044115]: hello world 1649703587.0439868\n", + "[INFO] [1649703587.144096]: hello world 1649703587.1439736\n", + "[INFO] [1649703587.243996]: hello world 1649703587.2438602\n", + "[INFO] [1649703587.344740]: hello world 1649703587.3445506\n", + "[INFO] [1649703587.444432]: hello world 1649703587.4442916\n", + "[INFO] [1649703587.544288]: hello world 1649703587.5441515\n", + "[INFO] [1649703587.644858]: hello world 1649703587.6447291\n", + "[INFO] [1649703587.744680]: hello world 1649703587.7445433\n", + "[INFO] [1649703587.843986]: hello world 1649703587.8438463\n", + "[INFO] [1649703587.944105]: hello world 1649703587.9439712\n", + "[INFO] [1649703588.043959]: hello world 1649703588.0438254\n", + "[INFO] [1649703588.144286]: hello world 1649703588.1441615\n", + "[INFO] [1649703588.244163]: hello world 1649703588.2440376\n", + "[INFO] [1649703588.344424]: hello world 1649703588.344307\n", + "[INFO] [1649703588.444412]: hello world 1649703588.444288\n", + "[INFO] [1649703588.543946]: hello world 1649703588.5438247\n", + "[INFO] [1649703588.644587]: hello world 1649703588.644452\n", + "[INFO] [1649703588.744046]: hello world 1649703588.7439137\n", + "[INFO] [1649703588.844102]: hello world 1649703588.843966\n", + "[INFO] [1649703588.944003]: hello world 1649703588.9438694\n", + "[INFO] [1649703589.044418]: hello world 1649703589.0442686\n", + "[INFO] [1649703589.144087]: hello world 1649703589.1439233\n", + "[INFO] [1649703589.244035]: hello world 1649703589.24391\n", + "[INFO] [1649703589.344838]: hello world 1649703589.3447015\n", + "[INFO] [1649703589.444629]: hello world 1649703589.4445014\n", + "[INFO] [1649703589.544325]: hello world 1649703589.5441964\n", + "[INFO] [1649703589.643943]: hello world 1649703589.64382\n", + "[INFO] [1649703589.743969]: hello world 1649703589.7438495\n", + "[INFO] [1649703589.844036]: hello world 1649703589.84391\n", + "[INFO] [1649703589.943963]: hello world 1649703589.943822\n", + "[INFO] [1649703590.043915]: hello world 1649703590.043772\n", + "[INFO] [1649703590.144677]: hello world 1649703590.1445453\n", + "[INFO] [1649703590.247269]: hello world 1649703590.247132\n", + "[INFO] [1649703590.344189]: hello world 1649703590.3440537\n", + "[INFO] [1649703590.444888]: hello world 1649703590.4447503\n", + "[INFO] [1649703590.544033]: hello world 1649703590.543816\n", + "[INFO] [1649703590.643985]: hello world 1649703590.6438534\n", + "[INFO] [1649703590.744539]: hello world 1649703590.7443998\n", + "[INFO] [1649703590.843940]: hello world 1649703590.8438067\n", + "[INFO] [1649703590.944543]: hello world 1649703590.94442\n", + "[INFO] [1649703591.044259]: hello world 1649703591.0441284\n", + "[INFO] [1649703591.144084]: hello world 1649703591.143953\n", + "[INFO] [1649703591.244712]: hello world 1649703591.244528\n", + "[INFO] [1649703591.344157]: hello world 1649703591.343953\n", + "[INFO] [1649703591.444733]: hello world 1649703591.4445803\n", + "[INFO] [1649703591.544277]: hello world 1649703591.544146\n", + "[INFO] [1649703591.643990]: hello world 1649703591.643864\n", + "[INFO] [1649703591.746750]: hello world 1649703591.7465801\n", + "[INFO] [1649703591.844438]: hello world 1649703591.8442981\n", + "[INFO] [1649703591.944042]: hello world 1649703591.9438593\n", + "[INFO] [1649703592.044764]: hello world 1649703592.0446446\n", + "[INFO] [1649703592.143937]: hello world 1649703592.1438212\n", + "[INFO] [1649703592.244027]: hello world 1649703592.2439015\n", + "[INFO] [1649703592.344582]: hello world 1649703592.3444617\n", + "[INFO] [1649703592.444562]: hello world 1649703592.4444134\n", + "[INFO] [1649703592.544601]: hello world 1649703592.5444767\n", + "[INFO] [1649703592.644390]: hello world 1649703592.6442592\n", + "[INFO] [1649703592.744479]: hello world 1649703592.7443504\n", + "[INFO] [1649703592.844004]: hello world 1649703592.8438745\n", + "[INFO] [1649703592.944144]: hello world 1649703592.9440129\n", + "[INFO] [1649703593.044004]: hello world 1649703593.0438783\n", + "[INFO] [1649703593.144319]: hello world 1649703593.1441872\n", + "[INFO] [1649703593.244035]: hello world 1649703593.2439\n", + "[INFO] [1649703593.344733]: hello world 1649703593.3445861\n", + "[INFO] [1649703593.444456]: hello world 1649703593.4443219\n", + "[INFO] [1649703593.544103]: hello world 1649703593.543978\n", + "[INFO] [1649703593.644153]: hello world 1649703593.644022\n", + "[INFO] [1649703593.744651]: hello world 1649703593.7445247\n", + "[INFO] [1649703593.844088]: hello world 1649703593.8439188\n", + "[INFO] [1649703593.944054]: hello world 1649703593.943915\n", + "[INFO] [1649703594.044180]: hello world 1649703594.0440507\n", + "[INFO] [1649703594.144401]: hello world 1649703594.1442654\n", + "[INFO] [1649703594.244153]: hello world 1649703594.2440228\n", + "[INFO] [1649703594.344804]: hello world 1649703594.3446825\n", + "[INFO] [1649703594.444507]: hello world 1649703594.4443686\n", + "[INFO] [1649703594.544288]: hello world 1649703594.5441523\n", + "[INFO] [1649703594.643967]: hello world 1649703594.6438298\n", + "[INFO] [1649703594.744705]: hello world 1649703594.7445648\n", + "[INFO] [1649703594.844136]: hello world 1649703594.8440042\n", + "[INFO] [1649703594.943949]: hello world 1649703594.9438198\n", + "[INFO] [1649703595.043945]: hello world 1649703595.0438066\n", + "[INFO] [1649703595.144138]: hello world 1649703595.1440053\n", + "[INFO] [1649703595.244174]: hello world 1649703595.2440171\n", + "[INFO] [1649703595.344072]: hello world 1649703595.3439493\n", + "[INFO] [1649703595.443968]: hello world 1649703595.4438195\n", + "[INFO] [1649703595.544122]: hello world 1649703595.5439785\n", + "[INFO] [1649703595.644784]: hello world 1649703595.644645\n", + "[INFO] [1649703595.744507]: hello world 1649703595.7443671\n", + "[INFO] [1649703595.844228]: hello world 1649703595.8440845\n", + "[INFO] [1649703595.944000]: hello world 1649703595.9438598\n", + "[INFO] [1649703596.044731]: hello world 1649703596.0445924\n", + "[INFO] [1649703596.144488]: hello world 1649703596.1443522\n", + "[INFO] [1649703596.244178]: hello world 1649703596.2440479\n", + "[INFO] [1649703596.344472]: hello world 1649703596.3443499\n", + "[INFO] [1649703596.444282]: hello world 1649703596.4441495\n", + "[INFO] [1649703596.543897]: hello world 1649703596.5437639\n", + "[INFO] [1649703596.644122]: hello world 1649703596.6439836\n", + "[INFO] [1649703596.744036]: hello world 1649703596.7439003\n", + "[INFO] [1649703596.844874]: hello world 1649703596.8447247\n", + "[INFO] [1649703596.944041]: hello world 1649703596.9439178\n", + "[INFO] [1649703597.044187]: hello world 1649703597.0440657\n", + "[INFO] [1649703597.143984]: hello world 1649703597.1438465\n", + "[INFO] [1649703597.244471]: hello world 1649703597.2443454\n", + "[INFO] [1649703597.344199]: hello world 1649703597.344063\n", + "[INFO] [1649703597.443948]: hello world 1649703597.4438062\n", + "[INFO] [1649703597.544706]: hello world 1649703597.5445683\n", + "[INFO] [1649703597.644028]: hello world 1649703597.6438916\n", + "[INFO] [1649703597.744260]: hello world 1649703597.7441378\n", + "[INFO] [1649703597.844027]: hello world 1649703597.8438773\n", + "[INFO] [1649703597.944391]: hello world 1649703597.944246\n", + "[INFO] [1649703598.043984]: hello world 1649703598.0438564\n", + "[INFO] [1649703598.144063]: hello world 1649703598.1439302\n", + "[INFO] [1649703598.243956]: hello world 1649703598.2438202\n", + "[INFO] [1649703598.344597]: hello world 1649703598.344453\n", + "[INFO] [1649703598.444257]: hello world 1649703598.4441235\n", + "[INFO] [1649703598.544143]: hello world 1649703598.544005\n", + "[INFO] [1649703598.644299]: hello world 1649703598.6441665\n", + "[INFO] [1649703598.743890]: hello world 1649703598.7437687\n", + "[INFO] [1649703598.844095]: hello world 1649703598.8439589\n", + "[INFO] [1649703598.944077]: hello world 1649703598.9439344\n", + "[INFO] [1649703599.044135]: hello world 1649703599.0439878\n", + "[INFO] [1649703599.144809]: hello world 1649703599.1446803\n", + "[INFO] [1649703599.243994]: hello world 1649703599.2438617\n", + "[INFO] [1649703599.344418]: hello world 1649703599.3443\n", + "[INFO] [1649703599.444122]: hello world 1649703599.4440002\n", + "[INFO] [1649703599.545436]: hello world 1649703599.5452924\n", + "[INFO] [1649703599.644000]: hello world 1649703599.6438744\n", + "[INFO] [1649703599.744405]: hello world 1649703599.7442696\n", + "[INFO] [1649703599.844276]: hello world 1649703599.8441496\n", + "[INFO] [1649703599.944091]: hello world 1649703599.9439595\n", + "[INFO] [1649703600.044573]: hello world 1649703600.0444295\n", + "[INFO] [1649703600.144255]: hello world 1649703600.144075\n", + "[INFO] [1649703600.244398]: hello world 1649703600.244263\n", + "[INFO] [1649703600.344043]: hello world 1649703600.3439043\n", + "[INFO] [1649703600.444829]: hello world 1649703600.4446914\n", + "[INFO] [1649703600.544537]: hello world 1649703600.5444024\n", + "[INFO] [1649703600.643934]: hello world 1649703600.643804\n", + "[INFO] [1649703600.744119]: hello world 1649703600.7439923\n", + "[INFO] [1649703600.843994]: hello world 1649703600.8438601\n", + "[INFO] [1649703600.944572]: hello world 1649703600.944442\n", + "[INFO] [1649703601.044247]: hello world 1649703601.0441163\n", + "[INFO] [1649703601.144140]: hello world 1649703601.1439798\n", + "[INFO] [1649703601.244071]: hello world 1649703601.2439384\n", + "[INFO] [1649703601.344427]: hello world 1649703601.3442965\n", + "[INFO] [1649703601.444093]: hello world 1649703601.44396\n", + "[INFO] [1649703601.544099]: hello world 1649703601.543959\n", + "[INFO] [1649703601.643935]: hello world 1649703601.64379\n", + "[INFO] [1649703601.744633]: hello world 1649703601.744485\n", + "[INFO] [1649703601.844347]: hello world 1649703601.844198\n", + "[INFO] [1649703601.944046]: hello world 1649703601.943898\n", + "[INFO] [1649703602.044026]: hello world 1649703602.0438879\n", + "[INFO] [1649703602.144485]: hello world 1649703602.144345\n", + "[INFO] [1649703602.244288]: hello world 1649703602.2441258\n", + "[INFO] [1649703602.344046]: hello world 1649703602.3439016\n", + "[INFO] [1649703602.444697]: hello world 1649703602.4445505\n", + "[INFO] [1649703602.544472]: hello world 1649703602.5443277\n", + "[INFO] [1649703602.644046]: hello world 1649703602.64389\n", + "[INFO] [1649703602.743953]: hello world 1649703602.7438211\n", + "[INFO] [1649703602.843908]: hello world 1649703602.8437834\n", + "[INFO] [1649703602.944410]: hello world 1649703602.9442744\n", + "[INFO] [1649703603.044118]: hello world 1649703603.0439699\n", + "[INFO] [1649703603.143930]: hello world 1649703603.1437902\n", + "[INFO] [1649703603.244583]: hello world 1649703603.2444394\n", + "[INFO] [1649703603.344094]: hello world 1649703603.343921\n", + "[INFO] [1649703603.444046]: hello world 1649703603.443885\n", + "[INFO] [1649703603.544185]: hello world 1649703603.544063\n", + "[INFO] [1649703603.644999]: hello world 1649703603.6449\n", + "[INFO] [1649703603.744503]: hello world 1649703603.7443626\n", + "[INFO] [1649703603.844051]: hello world 1649703603.8439205\n", + "[INFO] [1649703603.943970]: hello world 1649703603.9438376\n", + "[INFO] [1649703604.043962]: hello world 1649703604.0438123\n", + "[INFO] [1649703604.144872]: hello world 1649703604.1447136\n", + "[INFO] [1649703604.244602]: hello world 1649703604.2444715\n", + "[INFO] [1649703604.344337]: hello world 1649703604.3442101\n", + "[INFO] [1649703604.444038]: hello world 1649703604.443898\n", + "[INFO] [1649703604.543999]: hello world 1649703604.5438435\n", + "[INFO] [1649703604.643993]: hello world 1649703604.6438444\n", + "[INFO] [1649703604.744401]: hello world 1649703604.7442708\n", + "[INFO] [1649703604.844421]: hello world 1649703604.8442771\n", + "[INFO] [1649703604.944129]: hello world 1649703604.9439964\n", + "[INFO] [1649703605.044275]: hello world 1649703605.0441546\n", + "[INFO] [1649703605.144170]: hello world 1649703605.1440454\n", + "[INFO] [1649703605.244002]: hello world 1649703605.2438612\n", + "[INFO] [1649703605.344028]: hello world 1649703605.3438869\n", + "[INFO] [1649703605.444662]: hello world 1649703605.444534\n", + "[INFO] [1649703605.544240]: hello world 1649703605.5441005\n", + "[INFO] [1649703605.644467]: hello world 1649703605.6443298\n", + "[INFO] [1649703605.744189]: hello world 1649703605.7440522\n", + "[INFO] [1649703605.844901]: hello world 1649703605.8447752\n", + "[INFO] [1649703605.944654]: hello world 1649703605.94451\n", + "[INFO] [1649703606.044369]: hello world 1649703606.044221\n", + "[INFO] [1649703606.144112]: hello world 1649703606.1439734\n", + "[INFO] [1649703606.244610]: hello world 1649703606.244436\n", + "[INFO] [1649703606.344040]: hello world 1649703606.3438995\n" + ] + } + ], + "source": [ + "\n", + "\n", + "import rospy\n", + "\n", + "from std_msgs.msg import String\n", + "\n", + "pub = rospy.Publisher('chatter', String, queue_size=10)\n", + "rospy.init_node('analyst_node')\n", + "rate = rospy.Rate(10) # 10hz\n", + "while not rospy.is_shutdown():\n", + " hello_str = \"hello world %s\" % rospy.get_time()\n", + " rospy.loginfo(hello_str)\n", + " pub.publish(hello_str)\n", + " rate.sleep()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6471e750-3d36-4610-9bb3-755572a99fc1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/analyst/workspace/build_CNN_with_pytorch.ipynb b/analyst/workspace/build_CNN_with_pytorch.ipynb index 71f4a411..b180bdd6 100644 --- a/analyst/workspace/build_CNN_with_pytorch.ipynb +++ b/analyst/workspace/build_CNN_with_pytorch.ipynb @@ -1,25 +1,293 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "1da96116-06fe-42b8-8c3c-1a4605855239", + "metadata": {}, + "source": [ + "Build CNN Classifier using Simulation data and test it using real images\n", + "==============================================================" + ] + }, + { + "cell_type": "markdown", + "id": "f57618d6-de3f-4c90-bbe7-fb6f18055647", + "metadata": {}, + "source": [ + "Get Train and Test data using the simulation\n", + "----------------------------------------------" + ] + }, + { + "cell_type": "markdown", + "id": "4e494d06-695e-4c9a-8518-a056fadf53aa", + "metadata": {}, + "source": [ + "\n", + "\n", + "
\n", + "This section of the tutorial assumes that you have a functional ISAAC simulation working.\n", + "If not, you'll have to use pre-generated data for this tutorial purpose (not recommended).\n", + "
\n", + "\n", + "Start the simulation:\n", + "\n", + "`roslaunch isaac sim.launch gzclient:=true pose:=\"10.5 -8 4.5 0 0 0 1\"`\n", + "\n", + "Spawn the object\n", + "\n", + "`roslaunch isaac_gazebo spawn_object.launch spawn:=sock`\n", + "\n", + "Add an object to the simulation to be the anomaly (in this case a sock):\n", + "\n", + "`rosrun img_analysis get_train_data -path_dataset $PATH_DATASET -vent_poses $VENT_POSES -other_poses $OTHER_POSES [OPTIONS]`\n", + "\n", + "Arguments:\n", + " - `path_dataset` - Path to where to save the datasets, mandatory to define.\n", + " - `vent_poses` - *.txt* file containing the vent poses\n", + " - `other_poses` - *.txt* file containing the other non-vent poses\n", + " - `robot_dist` - Robot's distance to vent, standard is 1m\n", + " - `train_pics_per_vent` - Number of pictures taken per vent/other for train data\n", + " - `test_pics_per_vent` - Number of pictures taken per vent/other for test data\n", + " \n", + " \n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "d1486d87-cb5e-499d-9da9-1666665df5c4", + "metadata": {}, + "source": [ + "Train CNN\n", + "-----------" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "1b6a6b4d-99b1-47ca-8cfd-a668539fdab8", + "id": "9df2443f-50cc-49a9-9b4e-2f1018fc6904", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "\n", + "# Parameters\n", + "data_dir = \"data/vent\" # specify data path\n", + "classes = [\"free\", \"obstacle\"] # specify the image classes\n", + "num_epochs = 30 # number of epochs to train\n", + "model_name = \"model_cnn.pt\" # saved model name\n", + "trace_model_name = \"traced_model_cnn.pt\" # saved traced model name" + ] }, { "cell_type": "code", "execution_count": null, - "id": "9df2443f-50cc-49a9-9b4e-2f1018fc6904", + "id": "097ce74d-b561-4aa4-b899-8301205457a2", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn.functional as F\n", + "import torchvision.transforms as transforms\n", + "from PIL import Image\n", + "from torch import nn, optim\n", + "from torchvision import datasets, models, transforms\n", + "\n", + "\n", + "\n", + "# Define transforms for the training data and testing data\n", + "train_transforms = transforms.Compose(\n", + " [\n", + " transforms.Resize([256, 256]),\n", + " transforms.RandomRotation(30),\n", + " transforms.RandomResizedCrop(224),\n", + " transforms.RandomHorizontalFlip(),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n", + " ]\n", + ")\n", + "\n", + "test_transforms = transforms.Compose(\n", + " [\n", + " transforms.Resize(224),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n", + " ]\n", + ")\n", + "\n", + "# Pass transforms in here, then run the next cell to see how the transforms look\n", + "train_data = datasets.ImageFolder(data_dir + \"/train\", transform=train_transforms)\n", + "test_data = datasets.ImageFolder(data_dir + \"/test\", transform=test_transforms)\n", + "\n", + "trainloader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)\n", + "testloader = torch.utils.data.DataLoader(test_data, batch_size=64)" + ] }, { "cell_type": "code", - "execution_count": 1, - "id": "097ce74d-b561-4aa4-b899-8301205457a2", + "execution_count": null, + "id": "7534df6b-0f37-4bce-afa5-b34275ef314b", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Visualize some of the data-----------------------\n", + "# helper function to un-normalize and display an image\n", + "def imshow(img):\n", + " img = img / 2 + 0.5 # unnormalize\n", + " plt.imshow(np.transpose(img, (1, 2, 0))) # convert from Tensor image\n", + "\n", + "\n", + "# obtain one batch of training images\n", + "dataiter = iter(trainloader)\n", + "images, labels = dataiter.next()\n", + "images = images.numpy() # convert images to numpy for display\n", + "# plot the images in the batch, along with the corresponding labels\n", + "fig = plt.figure(figsize=(25, 4))\n", + "# display 20 images\n", + "for idx in np.arange(20):\n", + " ax = fig.add_subplot(2, 10, idx + 1, xticks=[], yticks=[])\n", + " imshow(images[idx])\n", + " ax.set_title(classes[labels[idx]])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e83e333-f0c8-49c3-96bb-866b7619e3fd", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Use GPU if it's available\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "\n", + "model = models.densenet121(pretrained=True)\n", + "\n", + "# Freeze parameters so we don't backprop through them\n", + "for param in model.parameters():\n", + " param.requires_grad = False\n", + "\n", + "model.classifier = nn.Sequential(\n", + " nn.Linear(1024, 256),\n", + " nn.ReLU(),\n", + " nn.Dropout(0.2),\n", + " nn.Linear(256, 3),\n", + " nn.LogSoftmax(dim=1),\n", + ")\n", + "\n", + "criterion = nn.NLLLoss()\n", + "\n", + "# Only train the classifier parameters, feature parameters are frozen\n", + "optimizer = optim.Adam(model.classifier.parameters(), lr=0.003)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6afec4b9-6bbc-42ca-aae0-f9b2a6f3d42c", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "model.to(device)\n", + "\n", + "epochs = num_epochs\n", + "steps = 0\n", + "running_loss = 0\n", + "print_every = 5\n", + "test_loss_min = np.Inf\n", + "for epoch in range(epochs):\n", + " for inputs, labels in trainloader:\n", + " steps += 1\n", + " # Move input and label tensors to the default device\n", + " inputs, labels = inputs.to(device), labels.to(device)\n", + "\n", + " optimizer.zero_grad()\n", + "\n", + " logps = model.forward(inputs)\n", + " loss = criterion(logps, labels)\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " running_loss += loss.item()\n", + "\n", + " if steps % print_every == 0:\n", + " test_loss = 0\n", + " accuracy = 0\n", + " model.eval()\n", + " with torch.no_grad():\n", + " for inputs, labels in testloader:\n", + " inputs, labels = inputs.to(device), labels.to(device)\n", + " logps = model.forward(inputs)\n", + " batch_loss = criterion(logps, labels)\n", + "\n", + " test_loss += batch_loss.item()\n", + "\n", + " # Calculate accuracy\n", + " ps = torch.exp(logps)\n", + " top_p, top_class = ps.topk(1, dim=1)\n", + " equals = top_class == labels.view(*top_class.shape)\n", + " accuracy += torch.mean(equals.type(torch.FloatTensor)).item()\n", + "\n", + " print(\n", + " \"Epoch \",\n", + " epoch + 1,\n", + " \"/\",\n", + " epochs,\n", + " \".. \" \"Train loss: \",\n", + " running_loss / print_every,\n", + " \".. \" \"Test loss: \",\n", + " test_loss / len(testloader),\n", + " \".. \" \"Test accuracy: \",\n", + " accuracy / len(testloader),\n", + " \"\",\n", + " )\n", + " running_loss = 0\n", + "\n", + " # save model if validation loss has decreased\n", + " if test_loss <= test_loss_min:\n", + " torch.save(model.state_dict(), model_name)\n", + " test_loss_min = test_loss\n", + "\n", + " model.to(\"cpu\")\n", + " # An example input you would normally provide to your model's forward() method.\n", + " example = torch.rand(1, 3, 224, 224)\n", + " # Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing.\n", + " traced_script_module = torch.jit.trace(model, example)\n", + " output = traced_script_module(torch.ones(1, 3, 224, 224))\n", + " print(output)\n", + " traced_script_module.save(trace_model_name)\n", + " model.to(device)\n", + "\n", + " model.train()\n" + ] + }, + { + "cell_type": "markdown", + "id": "899e03c7-91c9-43ef-9a1c-eb2b468a11dd", + "metadata": {}, + "source": [ + "Validate Result using Real data\n", + "------------------------------" + ] + }, + { + "cell_type": "markdown", + "id": "e24eb188-ee78-4de8-a7c8-f1001b0e65ba", + "metadata": {}, + "source": [ + "To validate the results using real data, let's use an image collected during ISS operations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f1c8f89-22cc-4c2f-8496-bda1e9add48a", "metadata": {}, "outputs": [], "source": [ @@ -34,16 +302,47 @@ "from torch import nn, optim\n", "from torchvision import datasets, models, transforms\n", "\n", + "# Open and display image\n", + "image = Image.open(\"data/bags/sock_iss.jpg\")\n", + "imgplot = plt.imshow(image)\n", + "plt.show()\n", "\n", + "# Open model\n", + "model = models.densenet121(pretrained=True)\n", + "model.classifier = nn.Sequential(\n", + " nn.Linear(1024, 256),\n", + " nn.ReLU(),\n", + " nn.Dropout(0.2),\n", + " nn.Linear(256, 3),\n", + " nn.LogSoftmax(dim=1),\n", + ")\n", + "model.load_state_dict(torch.load(\"model_cnn.pt\"))\n", + "model.eval()\n", "\n", + "# Classify Image!\n", "test_transforms = transforms.Compose(\n", " [\n", " transforms.Resize(224),\n", " transforms.ToTensor(),\n", " transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n", " ]\n", - ")" + ")\n", + "image_tensor = test_transforms(image).float()\n", + "image_tensor = image_tensor.unsqueeze_(0)\n", + "output = model(image_tensor)\n", + "\n", + "# Print Result\n", + "_, predicted = torch.max(output, 1)\n", + "print(\"Classification: \", classes[predicted])\n" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd58a98-6d15-4c07-8a9a-bd4c40f82d3f", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -62,7 +361,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.8.10" } }, "nbformat": 4, diff --git a/analyst/workspace/scripts/load_bag_database.py b/analyst/workspace/scripts/load_bag_database.py index d46477f7..8ca2198f 100644 --- a/analyst/workspace/scripts/load_bag_database.py +++ b/analyst/workspace/scripts/load_bag_database.py @@ -37,51 +37,52 @@ class LoadBagDatabase: - def __init__(self, database, path, topics): + def __init__(self, database, path, topics=[]): self.db = database - # TODO Marina: Recursively look into bag directory # Check the folder contents bagfiles = [f for f in listdir(path) if f.endswith(".bag")] - print("bagfiles" + str(bagfiles) + "\ntopics" + str(topics)) for bag in bagfiles: self.read_bag(path + bag, topics) - # TODO Marina: automatically joint bags split into different parts def read_bag(self, bag_file, topics_list): # access bag - print("Reading bag file") + print("Reading bag file ", bag_file) bag = rosbag.Bag(bag_file) - bag_contents = bag.read_messages() - bagName = bag.filename + topic_count = {} + messages_total = bag.get_message_count(topics_list) + message_count = 0 + # Go through all the messages in a topic + for subtopic, msg, t in bag.read_messages(topics_list): + # Print loading status + message_count = message_count + 1 + print("Reading ", message_count, "/", messages_total, " ", end="\r") - # get list of topics from the bag - bag_topics = [] - for topic, msg, t in bag_contents: - if topic in topics_list: - bag_topics.append(topic) - print("List of topics " + str(bag_topics)) + # Fix topic name + subtopic = subtopic[1:].replace("/", "_") - # create a new collection with the bag name - # collection name needs to start with a letter - bagFile = "bag_" + bagFile[:-4] - print("Creating collection " + bagFile) - if not self.db.hasCollection("yo"): - self.db.createCollection(name="yo") - # ensure index - self.db["yo"].ensureSkiplistIndex(["header.stamp.secs"]) + # Save topic count for later output + if subtopic in topic_count.keys(): + topic_count[subtopic] = topic_count.get(subtopic) + 1 + else: + topic_count[subtopic] = 1 - # open - for topic_name in bag_topics: - # Go through all the messages in a topic - for subtopic, msg, t in bag.read_messages(topic_name): - msg = yaml.safe_load(str(msg)) + # Create topic collection if it doesn't exist already + if not self.db.hasCollection(subtopic): + self.db.createCollection(name=subtopic) + # Convert message to yaml + msg = yaml.safe_load(str(msg)) + # Upload to database + aql = ( + "INSERT " + + "{'message':" + + str(msg) + + "}" + + " INTO " + + subtopic + + " LET newDoc = NEW RETURN newDoc" + ) + queryResult = self.db.AQLQuery(aql) - aql = ( - "INSERT " - + str(msg) - + " INTO " - + "yo" - + " LET newDoc = NEW RETURN newDoc" - ) - queryResult = self.db.AQLQuery(aql) + print("\nTopics found:") + print(topic_count) bag.close() diff --git a/anomaly/image/include/img_analysis/img_vent.h b/anomaly/image/include/img_analysis/img_vent.h index 09a720b0..8e4c756c 100644 --- a/anomaly/image/include/img_analysis/img_vent.h +++ b/anomaly/image/include/img_analysis/img_vent.h @@ -31,7 +31,7 @@ // FSW includes #include -#include +#include #include #include #include diff --git a/anomaly/image/src/img_analysis_nodelet.cc b/anomaly/image/src/img_analysis_nodelet.cc index c1cf1c94..3299fda7 100755 --- a/anomaly/image/src/img_analysis_nodelet.cc +++ b/anomaly/image/src/img_analysis_nodelet.cc @@ -60,13 +60,13 @@ class ImageAnalysisNode : public ff_util::FreeFlyerNodelet { // [0] fsm_.Add(STATE::WAITING, IMAGE_WAIT, [this](FSM::Event const& event) -> FSM::State { - ROS_ERROR_STREAM("CHANGE STATE TO ANALYSING"); + ROS_DEBUG_STREAM("CHANGE STATE TO ANALYSING"); return STATE::ANALYSING; }); // [1] fsm_.Add(STATE::ANALYSING, IMAGE_ANALYSED, [this](FSM::Event const& event) -> FSM::State { - ROS_ERROR_STREAM("CHANGE STATE TO WAITING"); + ROS_DEBUG_STREAM("CHANGE STATE TO WAITING"); return STATE::WAITING; }); } @@ -162,7 +162,7 @@ class ImageAnalysisNode : public ff_util::FreeFlyerNodelet { // A new arm action has been called void GoalCallback(isaac_msgs::ImageInspectionGoalConstPtr const& goal) { - ROS_ERROR_STREAM("RECEIVED()"); + ROS_DEBUG_STREAM("Received new Goal"); goal_ = *goal; sci_cam_req_ = true; return fsm_.Update(IMAGE_WAIT); @@ -180,14 +180,13 @@ class ImageAnalysisNode : public ff_util::FreeFlyerNodelet { case STATE::ANALYSING: feedback.state = STATE::ANALYSING; break; } - ROS_ERROR_STREAM("State changed to " << feedback.state); + ROS_DEBUG_STREAM("State changed to " << feedback.state); // Send the feedback if needed switch (state) { case STATE::WAITING: break; default: { - ROS_ERROR_STREAM("send feedback"); server_.SendFeedback(feedback); } } @@ -216,12 +215,12 @@ class ImageAnalysisNode : public ff_util::FreeFlyerNodelet { // Preempt the current action with a new action void PreemptCallback() { - ROS_ERROR("PreemptCallback"); + ROS_DEBUG_STREAM("PreemptCallback"); } // A Cancellation request arrives void CancelCallback() { - ROS_ERROR("CancelCallback"); + ROS_DEBUG_STREAM("CancelCallback"); } private: diff --git a/anomaly/image/tools/get_train_data_vent.cc b/anomaly/image/tools/get_train_data_vent.cc index 04301b6f..fc896b5e 100644 --- a/anomaly/image/tools/get_train_data_vent.cc +++ b/anomaly/image/tools/get_train_data_vent.cc @@ -30,7 +30,7 @@ #include // FSW includes -#include +#include #include #include #include diff --git a/astrobee/behaviors/cargo/readme.md b/astrobee/behaviors/cargo/readme.md index 4bae028f..46511381 100644 --- a/astrobee/behaviors/cargo/readme.md +++ b/astrobee/behaviors/cargo/readme.md @@ -2,7 +2,7 @@ This directory provides the cargo_tool -### Using the cargo tool +# Using the cargo tool This tool is used to initiate pickup and drop cargo actions. diff --git a/astrobee/behaviors/cargo/tools/cargo_tool.cc b/astrobee/behaviors/cargo/tools/cargo_tool.cc index 2a9c271e..3e707ef5 100644 --- a/astrobee/behaviors/cargo/tools/cargo_tool.cc +++ b/astrobee/behaviors/cargo/tools/cargo_tool.cc @@ -27,7 +27,7 @@ // FSW includes #include -#include +#include #include #include diff --git a/astrobee/behaviors/inspection/CMakeLists.txt b/astrobee/behaviors/inspection/CMakeLists.txt index 03499531..5b98b158 100644 --- a/astrobee/behaviors/inspection/CMakeLists.txt +++ b/astrobee/behaviors/inspection/CMakeLists.txt @@ -61,6 +61,9 @@ include_directories( add_library(inspection src/inspection_node.cc src/inspection.cc + src/anomaly_survey.cc + src/panorama_survey.cc + src/camera_projection.cc ) add_dependencies(inspection ${catkin_EXPORTED_TARGETS}) target_link_libraries(inspection ${catkin_LIBRARIES}) @@ -84,6 +87,12 @@ add_dependencies(export_panorama ${catkin_EXPORTED_TARGETS}) target_link_libraries(export_panorama inspection gflags ${catkin_LIBRARIES}) +## Declare a C++ executable: test_pano +add_executable(test_pano tools/test_pano.cc) +add_dependencies(test_pano ${catkin_EXPORTED_TARGETS}) +target_link_libraries(test_pano + inspection gflags ${catkin_LIBRARIES}) + ############# ## Install ## ############# @@ -121,4 +130,6 @@ install(FILES nodelet_plugins.xml install(TARGETS inspection_tool RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) install(TARGETS sci_cam_tool - RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) \ No newline at end of file + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) +install(TARGETS test_pano + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) diff --git a/astrobee/behaviors/inspection/include/inspection/inspection.h b/astrobee/behaviors/inspection/include/inspection/inspection.h old mode 100755 new mode 100644 index a19a33fe..e501b748 --- a/astrobee/behaviors/inspection/include/inspection/inspection.h +++ b/astrobee/behaviors/inspection/include/inspection/inspection.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,6 @@ // Software messages #include -#include #include #include @@ -67,22 +67,83 @@ // C++ headers #include +#include +#include /** * \ingroup beh */ namespace inspection { +/* + This class provides camera functionality that allows us + to project the 3D point into the camera frame and the + other way around. It automatically reads the camera + parameters from the config files based on the camera + name, such that no setup is necessary. +*/ +class CameraView { + public: + // Constructor + explicit CameraView(std::string cam_name, float f = 1.0, float n = 0.19); + + Eigen::Matrix4d GetProjectionMatrix(); + double GetHFOV(); + double GetVFOV(); + + double GetH(); + double GetW(); + + // Gets the points x y where the point is in the image. If outside the image, then it will return false + // If the robot pose is not specified, it's considered to be the current one + bool GetCamXYFromPoint(const geometry_msgs::Pose robot_pose, const geometry_msgs::Point point, int &x, int &y); + bool GetCamXYFromPoint(const geometry_msgs::Point point, int &x, int &y); + + bool GetPointFromXYD(const sensor_msgs::PointCloud2 pCloud, const int u, const int v, geometry_msgs::Point &point); + + double GetDistanceFromTarget(const geometry_msgs::Pose point, std::string depth_cam_name, + double size_x, double size_y); + + void DrawCameraFrustum(const geometry_msgs::Pose robot_pose, ros::Publisher &publisher); + + bool debug_ = false; + float f_; + float n_; + + protected: + bool SetProjectionMatrix(Eigen::Matrix3d cam_mat); + bool InsideTarget(std::vector vert_x, std::vector vert_y, int test_x, int test_y); + + private: + std::string cam_name_; + config_reader::ConfigReader cfg_cam_; + int W_, H_; + float fx_, fy_; + Eigen::Matrix4d P_; + + tf2_ros::Buffer tf_buffer_; + std::shared_ptr tf_listener_; + + geometry_msgs::TransformStamped tf_body_to_cam_; + + public: + // This fixes the Eigen aligment issue + // http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + /* This class provides the high-level logic that allows the freeflyer to - define the optimal inspection pose. It evaluates: + define the inspection poses for the different survey types. + It calls the survey generator and evaluates plans regarding: - * Visibility constraints + * Visibility constraints (anomaly inspection only) * Keepout and Keepin zones * Obstacle map - It returns a vector of possible inspection poses that can be updated - in case the move action fails due to planning or unmapped obstacle. + It returns a vector of inspection poses. "In the case of an anomaly inspection, + if the move action fails due to planning or an unmapped obstacle, it saves the + sorted alternatives such that replanning isn't necessary. It also constains functions that allow inspection visualization. */ class Inspection { @@ -91,15 +152,23 @@ class Inspection { Inspection(ros::NodeHandle* nh, ff_util::ConfigServer* cfg); // Read parameters from config server void ReadParam(); - // Generate inspection segment - bool GenSegment(geometry_msgs::Pose goal); + // Remove head of segment if planing failed bool RemoveInspectionPose(); // Get te head of the inspection poses segment - geometry_msgs::Pose GetInspectionPose(); + geometry_msgs::PoseArray GetCurrentInspectionPose(); + bool NextInspectionPose(); + bool RedoInspectionPose(); + geometry_msgs::PoseArray GetInspectionPoses(); + + // Get distance from camera to target + double GetDistanceToTarget(); - // Generate the survey for panorama pictures - void GeneratePanoramaSurvey(geometry_msgs::PoseArray &points_panorama); + // Generate the supported inspection methods + bool GenerateAnomalySurvey(geometry_msgs::PoseArray &points_anomaly); + bool GenerateGeometrySurvey(geometry_msgs::PoseArray &points_geometry); + bool GeneratePanoramaSurvey(geometry_msgs::PoseArray &points_panorama); + bool GenerateVolumetricSurvey(geometry_msgs::PoseArray &points_volume); protected: // Ensure all clients are connected @@ -108,64 +177,79 @@ class Inspection { void CheckZonesTimeoutCallback(); // Timeout on a map check request void CheckMapTimeoutCallback(); - // This function generates a sorted list based on the max viewing angle and resolution - bool GenerateSortedList(geometry_msgs::PoseArray &points); - // This function transforms the points from the camera rf to the body rf - bool TransformList(geometry_msgs::PoseArray points_in, geometry_msgs::PoseArray &points_out, - tf2::Transform target_transform); + // Checks the given points agains whether the target is visible // from a camera picture - bool VisibilityConstraint(geometry_msgs::PoseArray &points); + bool VisibilityConstraint(geometry_msgs::PoseArray &points, tf2::Transform target_transform); bool PointInsideCuboid(geometry_msgs::Point const& x, geometry_msgs::Vector3 const& cubemin, geometry_msgs::Vector3 const& cubemax); bool ZonesConstraint(geometry_msgs::PoseArray &points); bool ObstaclesConstraint(geometry_msgs::PoseArray &points); + // This function transforms the points from the camera rf to the body rf + bool TransformList(geometry_msgs::PoseArray points_in, geometry_msgs::PoseArray &points_out, + tf2::Transform target_transform); + // Draws the possible inspection poses - void DrawInspectionPoses(geometry_msgs::PoseArray &points, + void DrawPoseMarkers(geometry_msgs::PoseArray &points, ros::Publisher &publisher); // Draws visibility frostum projection - void DrawInspectionFrostum(); + void DrawCameraFrustum(); + + // This function generates a sorted list based on the max viewing angle and resolution + bool GenerateSortedList(geometry_msgs::PoseArray &points); private: ff_util::FreeFlyerServiceClient client_z_; ff_util::FreeFlyerServiceClient client_o_; + tf2_ros::Buffer tf_buffer_; std::shared_ptr tf_listener_; - ros::Publisher pub_; - tf2::Quaternion target_to_scicam_rot_; - geometry_msgs::PoseArray points_; // Vector containing inspection poses + // General inspection variables + std::string mode_; + int inspection_counter_; + + geometry_msgs::PoseArray goal_; // Vector containing inspection goals + std::vector points_; // Vector containing inspection poses + tf2::Quaternion target_to_cam_rot_; + + // Camera Projection functions + std::string curr_camera_; + std::map cameras_; // Parameter clients ff_util::ConfigServer *cfg_; - config_reader::ConfigReader cfg_cam_; // Inspection parameters - double opt_distance_; - double dist_resolution_; - double angle_resolution_; - double max_angle_; - double max_distance_; - double min_distance_; double horizontal_fov_; double aspect_ratio_; double target_size_x_; double target_size_y_; + std::string depth_cam_; // Panorame parameters + bool auto_fov_; double pan_min_; double pan_max_; double tilt_min_; double tilt_max_; + double h_fov_; + double v_fov_; + double att_tol_; double overlap_; // Publish Markers - ros::Publisher pub_no_filter_; - ros::Publisher pub_vis_check_; - ros::Publisher pub_zones_check_; - ros::Publisher pub_map_check_; + ros::Publisher pub_targets_; + ros::Publisher pub_markers_; + ros::Publisher pub_cam_; + + public: + // This fixes the Eigen aligment issue + // http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; + } // namespace inspection #endif // INSPECTION_INSPECTION_H_ diff --git a/astrobee/behaviors/inspection/include/inspection/panorama_survey.h b/astrobee/behaviors/inspection/include/inspection/panorama_survey.h new file mode 100755 index 00000000..c1f42f73 --- /dev/null +++ b/astrobee/behaviors/inspection/include/inspection/panorama_survey.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2021, 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. + */ + +#ifndef INSPECTION_PANORAMA_SURVEY_H_ +#define INSPECTION_PANORAMA_SURVEY_H_ + +#include +#include +#include +#include + +#define RAD_FROM_DEG(x) ((x) * M_PI / 180.0) +#define DEG_FROM_RAD(x) ((x) * 180.0 / M_PI) + +namespace inspection { + +class PanoAttitude { + public: + double pan; // radians, 0=forward, increases to the right + double tilt; // radians, 0=forward, increases upward + int16_t iy; // row index, 0=top row + int16_t ix; // column index, 0=left column + + inline PanoAttitude(double _pan, double _tilt, int16_t _iy, int16_t _ix) : + pan(_pan), + tilt(_tilt), + iy(_iy), + ix(_ix) + {} +}; + +typedef std::pair OrientLookupKey; +typedef std::pair OrientLookupValue; +typedef std::unordered_map > OrientLookupMap; + +void get_orient_lookup(OrientLookupMap* orient_lookup_out, + const std::vector& orientations); + +void GeneratePanoOrientations(std::vector* orientations_out, + int* nrows_out, + int* ncols_out, + double pan_radius, double tilt_radius, + double h_fov, double v_fov, + double overlap, double attitude_tolerance); + +} // namespace inspection + +#endif // INSPECTION_PANORAMA_SURVEY_H_ diff --git a/astrobee/behaviors/inspection/panorama_coverage_planning.md b/astrobee/behaviors/inspection/panorama_coverage_planning.md new file mode 100644 index 00000000..130bf6a5 --- /dev/null +++ b/astrobee/behaviors/inspection/panorama_coverage_planning.md @@ -0,0 +1,223 @@ +\page pano_coverage Panorama coverage planning + +A panorama coverage plan is a sequence of image center pan/tilt +values. The objective of panorama coverage planning is to generate a +plan that completely covers the specified range of pan/tilt values with +sufficient image-to-image overlap to permit downstream processing (e.g., +Hugin panorama stitching), and is sufficiently robust to attitude +error. Within those constraints, we want to optimize the plan for +minimum image count, as a proxy for minimum run time. + +# Relevant files + +- [pano_orientations.py](./scripts/pano_orientations.py): This was the + initial reference implementation of a panorama planner. It has been + superseded by improvements in the C++ code and may eventually go away. +- [pano.cc](./src/pano.cc): The latest C++ implementation of the + coverage planner. +- [test_pano.cc](./tools/test_pano.cc): A tool that invokes the coverage + planner on the specified test cases. +- [pano_test_cases.csv](./scripts/pano_test_cases.csv): The test cases + used by `test_pano.cc`. +- [plot_pano.py](./scripts/plot_pano.py): A script that checks the + output of the `test_pano` tool and produces plots for debugging. +- [field_of_view_calculator.py](./scripts/field_of_view_calculator.py): + A script to calculate the field of view for various Astrobee cameras. + +# Panorama design approach + +Panorama planning starts from the concept of a rectangular grid of image +centers, evenly spaced so as to completely cover the specified +(rectangular) imaging area with the desired overlap and attitude +tolerance. + +The collection order of images in the grid follows a column-major +raster pattern: alternating columns are collected top-to-bottom and +bottom-to-top. Column-major is preferred because it makes it easier +for crew to stay behind the robot during panorama collection, if they +care to do so. Following an alternating raster pattern minimizes +large attitude changes that are challenging for Astrobee localization. + +A further consideration is that, when viewing a plot of the coverage +using a typical equirectangular projection, the rectangular area of each +image becomes increasingly warped as the tilt value approaches the poles +at +/-90 degrees (Fig. 1). + +| ![Image warping](/doc/images/plot_3_one_column_borders.png "Image warping") | +|:--:| +| Figure 1: Image warping. Note that the red image FOV farther from tilt=0 is more warped than the green image FOV. | + +The primary effect of the warping is to make the effective image +coverage wider near the poles. We take advantage of this effect by +reducing the number of images in grid rows near the poles. A downside +of reducing the image count is that the images no longer form a grid, so +the column-major raster sequencing is only approximate (Fig. 2). + +A secondary effect of the warping is that it complicates determining how +to position the warped rectangles of individual image coverage so that +together they cover the boundaries of the rectangular desired imaging +area. As a result, although the panorama planner's simple heuristic +image spacing algorithm tries to meet the coverage and overlap +requirements, it can *not* guarantee they are satisfied in +general. Instead, you are encouraged to use the `test_pano` tool and +`plot_pano.py` script together to check correctness, and if there is a +problem, inflate the `plan_attitude_tolerance_degrees` parameter (used +by the `test_pano` tool at planning time) while leaving unchanged the +`test_attitude_tolerance_degrees` parameter (used by the `plot_pano.py` +tool at testing time), until the problem is corrected. + +# ISAAC panorama survey parameters + +There are different styles of panorama that could be useful for +different applications. + +## `5_mapper_and_hugin` + +The parameters in this test case are recommended as a potential +"workhorse" panorama type for doing complete module surveys. The design +criteria were: + +1. Capture a panorama with complete spherical coverage. +2. Tolerate up to 5 degrees (TBC) of attitude error without violating + complete coverage or overlap requirements. We don't yet have a good + estimate of actual attitude error during panorama collection. +3. Enable Hugin auto-stitch of a SciCam panorama. This requires complete + SciCam coverage with at least 30% overlap. This is the strictest + spacing criterion that ends up driving the panorama plan. +4. Enable post-activity bundle adjustment for localization. This + requires complete NavCam coverage with sufficient overlap. At least + 75-80% overlap is needed between consecutive NavCam images in time + sequence, but because the NavCam images are recorded continuously at + ~5 Hz, as long as the robot angular velocity is low enough, this + requirement will always be met regardless of the panorama plan. It + also helps to have > ~30% overlap between adjacent columns of the + panorama, which is not difficult because the NavCam FOV is so + wide. [Not the strictest/driving requirement.] +5. Enable ISAAC geometry mapper dense mapping to produce a 3D mesh with + SciCam texture. This requires both the NavCam bundle adjustment + listed above for registration, as well as complete SciCam and HazCam + coverage, but without a requirement for them to overlap. [Not the + strictest/driving requirement.] + +As of this writing (2022/02), using the experimental +`pano_orientations2()` planner, the resulting panorama plan has 56 +images in 7 rows, with at most 10 images in a row (Fig. 2). + +| ![5_mapper_and_hugin sequence](/doc/images/plot_5_mapper_and_hugin_seq.png "5_mapper_and_hugin sequence") | +|:--:| +| Figure 2: `5_mapper_and_hugin` sequence | + +## `4_mapper` + +This test case examines the scenario of relaxing the Hugin auto-stitch +requirement #2 above. In that case, requirement #5 becomes the driving +requirement. Because the panorama motion is vertical and HazCam images +are acquired continuously at ~5 Hz, the HazCam vertical spacing is not a +driving constraint, even though the HazCam has a smaller FOV than the +SciCam. As a result, we specify the VFOV from the SciCam, the HFOV from +the the HazCam, and as a bit of a hack, we pad the tilt radius slightly +to make doubly sure the HazCam gets complete coverage near the poles, +despite its smaller VFOV. + +The resulting panorama plan has far fewer images than +`5_mapper_and_hugin`, 30 vs. 56, which is attractive. The downsides are +that it may not be compatible with Hugin auto-stitch (although it may be +feasible to pass pan/tilt parameters from NavCam bundle adjustment to +Hugin instead), and probably more importantly, it would be less robust +to excessive robot pointing error. If time permits, it might be useful +to try to capture a panorama in this more aggressive mode to evaluate +whether the data is sufficient for downstream analysis. + +## `6_nav_hugin` + +This test case examines the scenario of relaxing both requirements #2 +and #5 above, so that the NavCam overlap requirement #4 becomes the +driving requirement. HazCam and SciCam coverage would be incomplete, so +the geometry mapper could not build a full 3D mesh, but the resulting +NavCam imagery could be used to build a low-resolution NavCam panorama +with Hugin auto-stitch. + +The resulting panorama plan has only 15 images. This type of panorama +could occasionally be suitable for a fast low-resolution survey. + +# Validation + +The following shell commands can be used to validate the panorama planner +on the test cases: + +```console +ISAAC_DIR="$HOME/isaac" +cd "$ISAAC_DIR/src/astrobee/behaviors/inspection/scripts" +$ISAAC_DIR/devel/lib/inspection/test_pano 2 # pano_orientations2() planner +./plot_pano.py -v +``` +Panorama plans must pass the following checks: + +- Complete coverage. A grid of test points are sampled from the full + spherical coverage (currently at 5 degree spacing). Each sampled test + point must be within the FOV of at least one image in the panorama. + (This testing approach is likely to catch coverage gaps, but can't + provide a guarantee for gaps sized smaller than the grid spacing.) + +- Sufficient overlap. Each image must have the required overlap + proportion with its immediate neighbors in the image grid, in all four + cardinal directions. Because columns don't necessarily align, there + may be two bracketing "immediate neighbors" in each of the north/south + directions, and the requirement is that the *total* overlap over both + neighbors must meet the specified overlap proportion. The overlap + proportion is estimated by sampling a test grid within the first image + and checking how many of the points fall within the neighbor image. + +- Attitude tolerance. An attitude tolerance requirement of *a* is + factored into the checks by (1) shrinking each image FOV by *a*/2 on + all sides, and (2) also padding the required coverage area by *a*/2 on + all sides. + +As of this writing, all test cases in `pano_test_cases.csv` pass with the +`pano_orientations2()` planner. + +During the validation process, `plot_pano.py` also writes several plots +for each test case that can be used to visualize the resulting panorama +plan. + +# Camera field of view estimation + +Modeling the field of view of a camera is complicated. The Astrobee +cameras of interest for panorama planning each have a rectangular sensor +and a lens with radial distortion. As a result, when the shape of the +camera FOV is displayed in a standard equirectangular projection, even +at tilt = 0, the FOV shape is not actually a rectangle, but instead has +a curved shape with "spikes at the corners". For the purposes of +panorama planning, the radial distortion effect is very significant for +the NavCam, somewhat significant for the HazCam, and almost negligible +for the SciCam. + +The true camera FOV shape is both complicated to calculate and difficult +to use for panorama planning purposes. As a result, our tools model the +FOV as a simplified rectangle. In particular, the rectangle dimensions +we use, as output by `field_of_view_calculator.py`, are an estimate of +the inscribed rectangle, i.e., the largest (axis-aligned) rectangle that +fits completely within the true FOV shape. This rectangular approximate +FOV is currently used both during panorama planning and validation. This +is a conservative approach in that it will underestimate the true +coverage and overlap in the panorama. + +Note that when camera FOV is reported elsewhere, such as in manufacturer +technical specifications, it may not agree with our dimensions in part +because they may use a different definition, such as the dimensions of +the circumscribing rectangle. + +Our FOV estimates are based on the calibrated camera intrinsics for +Bumble, as documented within the script. We expect that the FOV +variation between robots is not large enough to affect panorama +planning. + +Future improvements in this area could be: + +- Reimplement `field_of_view_calculator.py` in C++ using exactly the + same computer vision libraries used by Astrobee FSW, instead of the + current approach based on reimplementing some of the mathematical + formulas in simplified form. This would reduce the likelihood of + errors in the FOV estimation. +- Make the `plot_pano.py` script display the true camera FOV shape and + use that for validation. diff --git a/astrobee/behaviors/inspection/readme.md b/astrobee/behaviors/inspection/readme.md index 0fa674c5..60be67c3 100644 --- a/astrobee/behaviors/inspection/readme.md +++ b/astrobee/behaviors/inspection/readme.md @@ -2,14 +2,23 @@ This directory provides two tools: inspection_tool and sci_cam_tool. -Using the inspection tool ---------- +### Using the inspection tool + This tool is used to initiate inspection actions. To run the tool: rosrun inspection inspection_tool -$ACTION [OPTIONS] -As of now, the actions available are: +General parameters +| Parameter | Default value | Description | +| -------------------- | --------------------- | -------------------------------------------------------- | +| camera | "sci_cam" | Camera to use" | +| pos | "" | Desired position in cartesian format 'X Y Z' (meters) | +| att | "" | Desired attitude in RPY format 'roll pitch yaw' (degrees)| + +Once you launch the command, the actions available are: + +Inspection coordination: *pause*: Pauses the current action being performed, the goal is kept, so a resume command will resume the action. @@ -22,16 +31,68 @@ As of now, the actions available are: *save*: Saves the current goal, in the case flight software needs to be restarted. The current survey is saved in resources/current.txt. It can be loaded afterwards using -*anomaly*: Starts an anomaly inspection action. The robot will come up to a target, take a picture and run the picture through the image anomaly detector to classify it. + +Inspection modes: + + +*anomaly*: Starts an anomaly inspection action. The robot will come up to a target, take a picture and run the picture through the image anomaly detector to classify it. To specify the anomaly location, the position of the anomaly is needed and the attitude in which the robot should view it. The following figure shows an example of the target marker specified and how the robot positioned itself to view it: + +![Target reference frame](/doc/images/ref_frame_anomaly.png) + +An example command for this type of anomaly would be: + + rosrun inspection inspection_tool -anomaly -anomaly_poses /resources/inspection_iss.txt + +Relevant parameters: +| Parameter | Default value | Description | +| -------------------- | --------------------- | ------------------------------------------------------- | +| target_distance | 0.3, | Anomaly: desired distance to target (m) | +| min_distance | 0.2, | Anomaly: minimum distance to target (m) | +| max_distance | 0.7, | Anomaly: maximum distance to target (m) | +| max_angle | 40.0, | Anomaly: maximum angle to target (deg) | +| target_size_x | 0.05, | Anomaly: target size x - width (m) | +| target_size_y | 0.05, | Anomaly: target size y - height (m) | +| depth_cam | "haz", | Anomaly: depth cam to be used for distance measurements | +| toggle_flashlight | 0.0 (no toggle) | Anomaly: Toggle flashlight 0=OFF 1=MAX | +| focus_distance_step | 0.05 | Anomaly: Step to iterate focus distances (m) | +| focus_distance_range | 0.0, (no focus range) | Anomaly: Range when iterating focus distances (m) | + + *geometry*: Starts a geometry inspection, meaning that it will go to the commanded poses and take pictures to be processed by the geometry mapper. -*panorama*: it will do a panorama survey of all points specified +An example command for this type of anomaly would be: + + rosrun inspection inspection_tool -geometry -geometry_poses /resources/geometry_iss.txt + + +*panorama*: it will do a panorama survey of all points specified. For more information on how these surveys are generated, see page: \subpage pano_coverage. + +An example command for this type of anomaly would be: + + rosrun inspection inspection_tool -panorama -panorama_poses /resources/panorama_iss.txt + +Relevant parameters: +| Parameter | Default value | Description | +| -------------------- | --------------------- | -------------------------------------------------------- | +| panorama_mode | "" | Panorama configuration pre-set (from pano_test_cases.cvs) | +| pan_min | -180.0 | Panorama: minimum pan | +| pan_max | 180.0 | Panorama: maximum pan | +| tilt_min | -90.0 | Panorama: minimum tilt | +| tilt_max | 90.0 | Panorama: maximum tilt | +| h_fov | -1.0 | Panorama: camera horizontal fov, default -1 uses camera matrix | +| v_fov | -1.0 | Panorama: camera vertical fov, default -1 uses camera matrix | +| overlap | 0.5 | Panorama: overlap between images | +| att_tol | 5.0 | Panorama: attitude tolerance due to mobility | + *volumetric*: This will perform a volumetric survey -Using sci_cam_tool ---------- +An example command for this type of anomaly would be: + + rosrun inspection inspection_tool -volumetric -volumetric_poses /resources/volumetric_iss.txt + +### Using sci_cam_tool This tool is used to control the sci cam plugin in the Astrobee simulator, more precisely the way it acquires pictures. To use it, perform the following steps: @@ -58,3 +119,26 @@ The pictures will be published on the topic They will also show up in the sci cam window in RVIZ. If requests to take a single picture come at a high rate, some of them will be dropped. + +### Using export panorama tool + +This tool was created to allow for panorama surveys to be created and exported. This is useful to make panorama plans beforehand to ensure reproduceability. + +To export the panorama file: + + rosrun inspection export_panorama -panorama_poses $PANORAMA_POSES -panorama_out $OUTPUL_PLAN + +where $PANORAMA_POSES is a text file container the poses of the panorama centers, and $OUTPUT_PLAN is the output path of the panorama w.r.t. the package folder. +Other options that can be specified are: + + -camera (Camera to use) type: string default: "sci_cam" + -ns (Robot namespace) type: string default: "" + -overlap (Panorama: overlap between images) type: double default: 0.5 + -pan_max (Panorama: maximum pan) type: double default: 180 + -pan_min (Panorama: minimum pan) type: double default: -180 + -panorama_out (Panorama poses output) type: string + default: "/resources/pano_out.txt" + -panorama_poses (Panorama poses list to map) type: string + default: "/resources/scicam_panorama.txt" + -tilt_max (Panorama: maximum tilt) type: double default: 90 + -tilt_min (Panorama: minimum tilt) type: double default: -90 diff --git a/astrobee/behaviors/inspection/resources/geometry_granite_wannabee.txt b/astrobee/behaviors/inspection/resources/geometry_granite_wannabee.txt new file mode 100644 index 00000000..c9b38aff --- /dev/null +++ b/astrobee/behaviors/inspection/resources/geometry_granite_wannabee.txt @@ -0,0 +1,10 @@ +# Demo survey, simpler +-0.118 0.5 -0.7 180.0 0.0 0.0 +-0.118 0.0 -0.7 180.0 0.0 0.0 +-0.118 -0.5 -0.7 180.0 0.0 0.0 +0.5 0.118 -0.7 180.0 0.0 -90 +0.0 0.118 -0.7 180.0 0.0 -90 +-0.5 0.118 -0.7 180.0 0.0 -90 +0.118 -0.5 -0.7 180.0 0.0 -180 +0.118 0.0 -0.7 180.0 0.0 -180 +0.118 0.5 -0.7 180.0 0.0 -180 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/geometry_iss.txt b/astrobee/behaviors/inspection/resources/geometry_iss.txt new file mode 100644 index 00000000..7a864e9a --- /dev/null +++ b/astrobee/behaviors/inspection/resources/geometry_iss.txt @@ -0,0 +1,48 @@ +11.019643 -8.133377 4.381767 0.000000 10.407160 180.000000 +11.019643 -8.133377 4.555102 0.000000 0.000000 180.000000 +10.790432 -8.133377 4.682607 0.000000 44.816705 180.000000 +10.790432 -7.744343 4.682607 0.000000 44.816705 180.000000 +11.019643 -7.744343 4.381767 0.000000 10.407160 180.000000 +11.019643 -7.744343 4.555102 0.000000 0.000000 180.000000 +11.019643 -7.744343 4.938915 0.000000 0.000000 180.000000 +11.019643 -8.133377 4.938915 0.000000 0.000000 180.000000 +11.019643 -8.133377 5.322728 0.000000 0.000000 180.000000 +11.019643 -8.133377 5.496063 0.000000 -10.407163 180.000000 +11.019643 -7.744343 5.496063 0.000000 -10.407163 180.000000 +11.019643 -7.744343 5.322728 0.000000 0.000000 180.000000 +10.739543 -7.744343 5.144007 0.000000 -44.816721 180.000000 +10.739543 -8.133377 5.144007 0.000000 -44.816721 180.000000 +10.933000 -8.133377 4.859063 -180.000000 -89.999999 0.000000 +11.314369 -8.133377 4.859063 -180.000000 -89.999999 0.000000 +11.483357 -8.133377 4.859063 -180.000000 -79.500869 0.000000 +11.483357 -7.744343 4.859063 -180.000000 -79.500869 0.000000 +11.314369 -7.744343 4.859063 -180.000000 -89.999999 0.000000 +10.933000 -7.744343 4.859063 -180.000000 -89.999999 0.000000 +10.551631 -7.744343 4.859063 -180.000000 -89.999999 0.000000 +10.382643 -7.744343 4.859063 0.000000 -79.500869 180.000000 +10.382643 -8.133377 4.859063 0.000000 -79.500869 180.000000 +10.551631 -8.133377 4.859063 -180.000000 -89.999999 0.000000 +10.551631 -8.150723 5.018767 -0.000000 89.999999 0.000000 +10.382643 -8.150723 5.018767 180.000000 79.500869 180.000000 +10.382643 -7.760301 5.018767 180.000000 79.500869 180.000000 +10.551631 -7.760301 5.018767 -0.000000 89.999999 0.000000 +10.933000 -7.760301 5.018767 -0.000000 89.999999 0.000000 +11.314369 -7.760301 5.018767 -0.000000 89.999999 0.000000 +11.483357 -7.760301 5.018767 0.000000 79.500869 0.000000 +11.483357 -8.150723 5.018767 0.000000 79.500869 0.000000 +11.314369 -8.150723 5.018767 -0.000000 89.999999 0.000000 +10.933000 -8.150723 5.018767 -0.000000 89.999999 0.000000 +11.075568 -8.133377 4.682607 0.000000 44.816705 0.000000 +11.075568 -7.744343 4.682607 0.000000 44.816705 0.000000 +10.846357 -7.744343 4.555102 0.000000 0.000000 0.000000 +10.846357 -7.744343 4.381767 0.000000 10.407160 0.000000 +10.846357 -8.133377 4.381767 0.000000 10.407160 0.000000 +10.846357 -8.133377 4.555102 0.000000 0.000000 0.000000 +10.846357 -8.133377 4.938915 0.000000 0.000000 0.000000 +10.846357 -7.744343 4.938915 0.000000 0.000000 0.000000 +10.846357 -7.744343 5.322728 0.000000 0.000000 0.000000 +10.846357 -7.744343 5.496063 0.000000 -10.407163 0.000000 +10.846357 -8.133377 5.496063 0.000000 -10.407163 0.000000 +10.846357 -8.133377 5.322728 0.000000 0.000000 0.000000 +11.126457 -8.133377 5.144007 0.000000 -44.816721 0.000000 +11.126457 -7.744343 5.144007 0.000000 -44.816721 0.000000 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/inspection_granite.txt b/astrobee/behaviors/inspection/resources/inspection_granite.txt index fe4666de..88be9dde 100644 --- a/astrobee/behaviors/inspection/resources/inspection_granite.txt +++ b/astrobee/behaviors/inspection/resources/inspection_granite.txt @@ -1,2 +1,3 @@ # x y z roll pitch yaw -0.0 -1.0 -0.7 90 0 180 \ No newline at end of file +0.5 -1.0 -0.772 0 0 -90 +#1.0 0.5 -0.772 0 0 0 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/inspection_granite_wannabee.txt b/astrobee/behaviors/inspection/resources/inspection_granite_wannabee.txt new file mode 100644 index 00000000..d2656376 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/inspection_granite_wannabee.txt @@ -0,0 +1,3 @@ +# x y z roll pitch yaw +0.5 -1.0 -0.6 180 0 -90 +#1.0 0.5 -0.6 180 0 0 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/inspection_iss.txt b/astrobee/behaviors/inspection/resources/inspection_iss.txt index e785e19a..adbe255b 100644 --- a/astrobee/behaviors/inspection/resources/inspection_iss.txt +++ b/astrobee/behaviors/inspection/resources/inspection_iss.txt @@ -1,16 +1,18 @@ # x y z roll pitch yaw -10.2067169189 -3.1288216114 4.82276105881 -90 0.000000 180 -# 11.971072 -9.193897 3.963823 0.000000 -40 0.000000 -# 11.921994 -8.174384 3.923862 0.000000 -40 0.000000 -# 11.921994 -7.197721 3.923862 0.000000 -40 0.000000 -# 11.921994 -6.196085 3.923862 0.000000 -40 0.000000 -# 11.921994 -5.182941 3.923862 0.000000 -40 0.000000 -# 11.921994 -4.200116 3.923862 0.000000 -40 0.000000 -# 11.921994 -3.201995 3.923862 0.000000 -40 0.000000 -# 9.942000 -9.189370 3.910500 0.000000 40 0.000000 -# 9.942000 -8.189623 3.910500 0.000000 40 0.000000 -# 9.942000 -7.145759 3.910500 0.000000 40 0.000000 -# 9.942000 -6.190303 3.910500 0.000000 40 0.000000 -# 9.942000 -5.215301 3.910500 0.000000 40 0.000000 -# 9.942000 -4.210000 3.910500 0.000000 40 0.000000 -# 9.942000 -3.243441 3.910500 0.000000 40 0.000000 \ No newline at end of file +10.117551803588867 -3.126357078552246 4.45230770111084 0 0 90 + +# 11.921994 -3.201995 3.923862 0.0 40 0.0 +# 11.921994 -4.200116 3.923862 0.0 40 0.0 +# 11.921994 -5.182941 3.923862 0.0 40 0.0 +# 11.921994 -6.196085 3.923862 0.0 40 0.0 +# 11.921994 -7.197721 3.923862 0.0 40 0.0 +# 11.921994 -8.174384 3.923862 0.0 40 0.0 +# 11.971072 -9.193897 3.963823 0.0 40 0.0 + +# 9.942000 -8.189623 3.910500 0.0 40 180.0 +# 9.942000 -7.145759 3.910500 0.0 40 180.0 +# 9.942000 -6.190303 3.910500 0.0 40 180.0 +# 9.942000 -5.215301 3.910500 0.0 40 180.0 +# 9.942000 -4.210000 3.910500 0.0 40 180.0 +# 9.942000 -3.243441 3.910500 0.0 40 180.0 + diff --git a/astrobee/behaviors/inspection/resources/isaac10/nod2_bay2_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay2_std_panorama.txt new file mode 100644 index 00000000..c460d4f7 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay2_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 0.0509946 4.896 0 0 0.975141 -0.221587 +11.1315 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 0 4.94269 0 0.581753 0 0.813365 +10.9915 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 0 4.71935 0 -0.581753 0 0.813365 +10.9406 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -0.117679 4.896 0 0 0.680563 0.73269 +10.9842 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -0.102103 4.896 0 0 0.866398 0.499355 +11.0627 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -0.0509946 4.896 0 0 0.975141 0.221587 +11.0623 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac10/nod2_bay2_std_panorama_stereo.txt b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay2_std_panorama_stereo.txt new file mode 100644 index 00000000..a881a3a8 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay2_std_panorama_stereo.txt @@ -0,0 +1,108 @@ +11.0077 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +10.8077 .00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +10.8623 .0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 0.0509946 4.896 0 0 0.975141 -0.221587 +10.9064 .0509946 4.896 0 0 0.975141 -0.221587 +11.1315 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +10.9315 .0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +10.9268 .0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +10.8743 .105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +10.8397 .145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +10.8627 .132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +10.8592 .102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +10.8297 .0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.8024 .00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.7696 -.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.8243 -.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.7959 .00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.7842 .067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.7913 .117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.7667 .142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.7321 .134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.7406 .114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.6595 .0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.6851 .0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.7265 .0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.7456 .0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.7915 .00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.7306 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.6872 .0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.6534 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.6872 -.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 0 4.94269 0 0.581753 0 0.813365 +10.8527 0 4.94269 0 0.581753 0 0.813365 +10.9915 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.7915 -.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.7456 -.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.7265 -.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.6851 -.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.6595 -.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 0 4.71935 0 -0.581753 0 0.813365 +10.671 0 4.71935 0 -0.581753 0 0.813365 +10.9406 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.7406 -.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.7321 -.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.7667 -.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -0.117679 4.896 0 0 0.680563 0.73269 +10.7913 -.117679 4.896 0 0 0.680563 0.73269 +10.9842 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.7842 -.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.7959 -.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.8243 .0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.7696 .0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +10.8024 -.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +10.8297 -.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -0.102103 4.896 0 0 0.866398 0.499355 +10.8592 -.102103 4.896 0 0 0.866398 0.499355 +11.0627 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +10.8627 -.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +10.8397 -.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +10.8743 -.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +10.9268 -.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +10.9315 -.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -0.0509946 4.896 0 0 0.975141 0.221587 +10.9064 -.0509946 4.896 0 0 0.975141 0.221587 +11.0623 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +10.8623 -.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 +10.8077 -.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac10/nod2_bay3_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay3_std_panorama.txt new file mode 100644 index 00000000..fa503286 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay3_std_panorama.txt @@ -0,0 +1,54 @@ +10.0077 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +10.0623 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +10.1064 0.0509946 4.896 0 0 0.975141 -0.221587 +10.1315 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +10.1268 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +10.0743 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +10.0397 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +10.0627 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +10.0592 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +10.0297 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +10.0024 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +9.96961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.0243 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +9.99588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +9.98423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +9.99131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +9.96668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +9.93207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +9.94064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +9.85953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +9.88513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +9.92649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +9.94562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +9.99149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +9.93058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +9.88725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +9.85335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +9.88725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.0527 0 4.94269 0 0.581753 0 0.813365 +9.99149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +9.94562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +9.92649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +9.88513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +9.85953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +9.87102 0 4.71935 0 -0.581753 0 0.813365 +9.94064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +9.93207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +9.96668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +9.99131 -0.117679 4.896 0 0 0.680563 0.73269 +9.98423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +9.99588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.0243 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +9.96961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.0024 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +10.0297 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +10.0592 -0.102103 4.896 0 0 0.866398 0.499355 +10.0627 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +10.0397 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +10.0743 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +10.1268 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +10.1315 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +10.1064 -0.0509946 4.896 0 0 0.975141 0.221587 +10.0623 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +10.0077 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac10/nod2_bay3_std_panorama_stereo.txt b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay3_std_panorama_stereo.txt new file mode 100644 index 00000000..75c30fe6 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay3_std_panorama_stereo.txt @@ -0,0 +1,108 @@ +10.0077 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +10.2077 .00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +10.0623 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +10.2623 .0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +10.1064 0.0509946 4.896 0 0 0.975141 -0.221587 +10.3064 .0509946 4.896 0 0 0.975141 -0.221587 +10.1315 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +10.3315 .0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +10.1268 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +10.3268 .0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +10.0743 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +10.2743 .105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +10.0397 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +10.2397 .145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +10.0627 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +10.2627 .132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +10.0592 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +10.2592 .102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +10.0297 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +10.2297 .0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +10.0024 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.2024 .00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +9.96961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.16961 -.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.0243 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.2243 -.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +9.99588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.19588 .00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +9.98423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.18423 .067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +9.99131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.19131 .117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +9.96668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.16668 .142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +9.93207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.13207 .134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +9.94064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.14064 .114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +9.85953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.05953 .0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +9.88513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.08513 .0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +9.92649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.12649 .0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +9.94562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.14562 .0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +9.99149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.19149 .00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +9.93058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.13058 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +9.88725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.08725 .0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +9.85335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.05335 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +9.88725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.08725 -.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.0527 0 4.94269 0 0.581753 0 0.813365 +10.2527 0 4.94269 0 0.581753 0 0.813365 +9.99149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.19149 -.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +9.94562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.14562 -.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +9.92649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.12649 -.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +9.88513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.08513 -.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +9.85953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.05953 -.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +9.87102 0 4.71935 0 -0.581753 0 0.813365 +10.07102 0 4.71935 0 -0.581753 0 0.813365 +9.94064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.14064 -.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +9.93207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.13207 -.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +9.96668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.16668 -.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +9.99131 -0.117679 4.896 0 0 0.680563 0.73269 +10.19131 -.117679 4.896 0 0 0.680563 0.73269 +9.98423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.18423 -.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +9.99588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.19588 -.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.0243 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.2243 .0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +9.96961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.16961 .0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.0024 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +10.2024 -.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +10.0297 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +10.2297 -.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +10.0592 -0.102103 4.896 0 0 0.866398 0.499355 +10.2592 -.102103 4.896 0 0 0.866398 0.499355 +10.0627 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +10.2627 -.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +10.0397 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +10.2397 -.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +10.0743 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +10.2743 -.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +10.1268 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +10.3268 -.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +10.1315 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +10.3315 -.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +10.1064 -0.0509946 4.896 0 0 0.975141 0.221587 +10.3064 -.0509946 4.896 0 0 0.975141 0.221587 +10.0623 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +10.2623 -.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +10.0077 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 +10.2077 -.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac10/nod2_bay4_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay4_std_panorama.txt new file mode 100644 index 00000000..03a84464 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay4_std_panorama.txt @@ -0,0 +1,54 @@ +9.00768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +9.06226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +9.10641 0.0509946 4.896 0 0 0.975141 -0.221587 +9.13151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +9.12682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +9.07434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +9.03969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +9.06267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +9.05915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +9.02967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +9.00241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +8.96961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +9.02426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +8.99588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +8.98423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +8.99131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +8.96668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +8.93207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +8.94064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +8.85953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +8.88513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +8.92649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +8.94562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +8.99149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +8.93058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +8.88725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +8.85335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +8.88725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +9.05272 0 4.94269 0 0.581753 0 0.813365 +8.99149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +8.94562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +8.92649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +8.88513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +8.85953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +8.87102 0 4.71935 0 -0.581753 0 0.813365 +8.94064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +8.93207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +8.96668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +8.99131 -0.117679 4.896 0 0 0.680563 0.73269 +8.98423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +8.99588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +9.02426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +8.96961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +9.00241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +9.02967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +9.05915 -0.102103 4.896 0 0 0.866398 0.499355 +9.06267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +9.03969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +9.07434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +9.12682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +9.13151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +9.10641 -0.0509946 4.896 0 0 0.975141 0.221587 +9.06226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +9.00768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac10/nod2_bay4_std_panorama_stereo.txt b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay4_std_panorama_stereo.txt new file mode 100644 index 00000000..15b2e78b --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac10/nod2_bay4_std_panorama_stereo.txt @@ -0,0 +1,108 @@ +9.00768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +9.20768 .00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +9.06226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +9.26226 .0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +9.10641 0.0509946 4.896 0 0 0.975141 -0.221587 +9.30641 .0509946 4.896 0 0 0.975141 -0.221587 +9.13151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +9.33151 .0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +9.12682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +9.32682 .0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +9.07434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +9.27434 .105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +9.03969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +9.23969 .145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +9.06267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +9.26267 .132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +9.05915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +9.25915 .102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +9.02967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +9.22967 .0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +9.00241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +9.20241 .00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +8.96961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +9.16961 -.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +9.02426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +9.22426 -.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +8.99588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +9.19588 .00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +8.98423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +9.18423 .067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +8.99131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +9.19131 .117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +8.96668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +9.16668 .142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +8.93207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +9.13207 .134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +8.94064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +9.14064 .114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +8.85953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +9.05953 .0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +8.88513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +9.08513 .0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +8.92649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +9.12649 .0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +8.94562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +9.14562 .0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +8.99149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +9.19149 .00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +8.93058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +9.13058 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +8.88725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +9.08725 .0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +8.85335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +9.05335 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +8.88725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +9.08725 -.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +9.05272 0 4.94269 0 0.581753 0 0.813365 +9.25272 0 4.94269 0 0.581753 0 0.813365 +8.99149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +9.19149 -.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +8.94562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +9.14562 -.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +8.92649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +9.12649 -.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +8.88513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +9.08513 -.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +8.85953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +9.05953 -.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +8.87102 0 4.71935 0 -0.581753 0 0.813365 +9.07102 0 4.71935 0 -0.581753 0 0.813365 +8.94064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +9.14064 -.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +8.93207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +9.13207 -.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +8.96668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +9.16668 -.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +8.99131 -0.117679 4.896 0 0 0.680563 0.73269 +9.19131 -.117679 4.896 0 0 0.680563 0.73269 +8.98423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +9.18423 -.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +8.99588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +9.19588 -.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +9.02426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +9.22426 .0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +8.96961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +9.16961 .0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +9.00241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +9.20241 -.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +9.02967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +9.22967 -.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +9.05915 -0.102103 4.896 0 0 0.866398 0.499355 +9.25915 -.102103 4.896 0 0 0.866398 0.499355 +9.06267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +9.26267 -.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +9.03969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +9.23969 -.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +9.07434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +9.27434 -.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +9.12682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +9.32682 -.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +9.13151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +9.33151 -.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +9.10641 -0.0509946 4.896 0 0 0.975141 0.221587 +9.30641 -.0509946 4.896 0 0 0.975141 0.221587 +9.06226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +9.26226 -.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +9.00768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 +9.20768 -.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac11/usl_bay1_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac11/usl_bay1_std_panorama.txt new file mode 100644 index 00000000..31cbd5ed --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac11/usl_bay1_std_panorama.txt @@ -0,0 +1,54 @@ +4.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +4.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +4.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +4.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +4.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +4.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +4.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +4.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +4.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +4.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +4.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +4.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +4.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +4.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +4.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +4.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +4.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +4.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +4.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +4.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +4.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +4.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +4.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +4.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +4.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +4.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +4.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +4.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +4.70272 0 4.94269 0 0.581753 0 0.813365 +4.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +4.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +4.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +4.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +4.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +4.52102 0 4.71935 0 -0.581753 0 0.813365 +4.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +4.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +4.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +4.64131 -0.117679 4.896 0 0 0.680563 0.73269 +4.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +4.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +4.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +4.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +4.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +4.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +4.70915 -0.102103 4.896 0 0 0.866398 0.499355 +4.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +4.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +4.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +4.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +4.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +4.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +4.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +4.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac11/usl_bay2_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac11/usl_bay2_std_panorama.txt new file mode 100644 index 00000000..363d59b5 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac11/usl_bay2_std_panorama.txt @@ -0,0 +1,54 @@ +3.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +3.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +3.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +3.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +3.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +3.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +3.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +3.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +3.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +3.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +3.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +3.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +3.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +3.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +3.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +3.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +3.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +3.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +3.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +3.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +3.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +3.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +3.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +3.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +3.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +3.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +3.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +3.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +3.70272 0 4.94269 0 0.581753 0 0.813365 +3.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +3.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +3.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +3.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +3.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +3.52102 0 4.71935 0 -0.581753 0 0.813365 +3.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +3.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +3.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +3.64131 -0.117679 4.896 0 0 0.680563 0.73269 +3.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +3.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +3.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +3.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +3.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +3.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +3.70915 -0.102103 4.896 0 0 0.866398 0.499355 +3.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +3.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +3.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +3.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +3.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +3.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +3.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +3.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac11/usl_bay3_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac11/usl_bay3_std_panorama.txt new file mode 100644 index 00000000..72eb45a2 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac11/usl_bay3_std_panorama.txt @@ -0,0 +1,54 @@ +2.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +2.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +2.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +2.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +2.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +2.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +2.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +2.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +2.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +2.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +2.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +2.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +2.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +2.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +2.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +2.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +2.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +2.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +2.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +2.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +2.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +2.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +2.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +2.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +2.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +2.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +2.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +2.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +2.70272 0 4.94269 0 0.581753 0 0.813365 +2.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +2.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +2.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +2.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +2.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +2.52102 0 4.71935 0 -0.581753 0 0.813365 +2.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +2.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +2.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +2.64131 -0.117679 4.896 0 0 0.680563 0.73269 +2.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +2.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +2.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +2.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +2.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +2.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +2.70915 -0.102103 4.896 0 0 0.866398 0.499355 +2.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +2.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +2.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +2.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +2.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +2.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +2.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +2.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac11/usl_bay4_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac11/usl_bay4_std_panorama.txt new file mode 100644 index 00000000..1c1ffc57 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac11/usl_bay4_std_panorama.txt @@ -0,0 +1,54 @@ +1.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +1.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +1.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +1.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +1.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +1.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +1.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +1.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +1.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +1.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +1.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +1.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +1.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +1.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +1.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +1.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +1.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +1.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +1.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +1.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +1.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +1.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +1.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +1.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +1.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +1.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +1.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +1.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +1.70272 0 4.94269 0 0.581753 0 0.813365 +1.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +1.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +1.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +1.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +1.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +1.52102 0 4.71935 0 -0.581753 0 0.813365 +1.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +1.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +1.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +1.64131 -0.117679 4.896 0 0 0.680563 0.73269 +1.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +1.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +1.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +1.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +1.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +1.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +1.70915 -0.102103 4.896 0 0 0.866398 0.499355 +1.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +1.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +1.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +1.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +1.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +1.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +1.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +1.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac11/usl_bay5_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac11/usl_bay5_std_panorama.txt new file mode 100644 index 00000000..e635385d --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac11/usl_bay5_std_panorama.txt @@ -0,0 +1,54 @@ +0.657685 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +0.712256 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +0.756412 0.0509946 4.896 0 0 0.975141 -0.221587 +0.781507 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +0.776817 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +0.724338 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +0.689693 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +0.712675 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +0.709152 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +0.679671 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +0.652405 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +0.619614 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +0.674264 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +0.645884 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +0.634227 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +0.641307 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +0.616682 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +0.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +0.590639 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +0.509528 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +0.535128 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +0.576487 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +0.595619 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +0.641488 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +0.580577 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +0.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +0.503354 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +0.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +0.702721 0 4.94269 0 0.581753 0 0.813365 +0.641488 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +0.595619 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +0.576487 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +0.535128 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +0.509528 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +0.521021 0 4.71935 0 -0.581753 0 0.813365 +0.590639 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +0.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +0.616682 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +0.641307 -0.117679 4.896 0 0 0.680563 0.73269 +0.634227 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +0.645884 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +0.674264 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +0.619614 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +0.652405 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +0.679671 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +0.709152 -0.102103 4.896 0 0 0.866398 0.499355 +0.712675 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +0.689693 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +0.724338 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +0.776817 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +0.781507 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +0.756412 -0.0509946 4.896 0 0 0.975141 0.221587 +0.712256 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +0.657685 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac11/usl_bay6_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac11/usl_bay6_std_panorama.txt new file mode 100644 index 00000000..982e1c8c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac11/usl_bay6_std_panorama.txt @@ -0,0 +1,54 @@ +-0.342315 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +-0.287744 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +-0.243588 0.0509946 4.896 0 0 0.975141 -0.221587 +-0.218493 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +-0.223183 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +-0.275662 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +-0.310307 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +-0.287325 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +-0.290848 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +-0.320329 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +-0.347595 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +-0.380386 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +-0.325736 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +-0.354116 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +-0.365773 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +-0.358693 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +-0.383318 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +-0.41793 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +-0.409361 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +-0.490472 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +-0.464872 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +-0.423513 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +-0.404381 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +-0.358512 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +-0.419423 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +-0.46275 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +-0.496646 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +-0.46275 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +-0.297279 0 4.94269 0 0.581753 0 0.813365 +-0.358512 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +-0.404381 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +-0.423513 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +-0.464872 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +-0.490472 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +-0.478979 0 4.71935 0 -0.581753 0 0.813365 +-0.409361 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +-0.41793 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +-0.383318 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +-0.358693 -0.117679 4.896 0 0 0.680563 0.73269 +-0.365773 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +-0.354116 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +-0.325736 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +-0.380386 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +-0.347595 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +-0.320329 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +-0.290848 -0.102103 4.896 0 0 0.866398 0.499355 +-0.287325 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +-0.310307 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +-0.275662 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +-0.223183 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +-0.218493 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +-0.243588 -0.0509946 4.896 0 0 0.975141 0.221587 +-0.287744 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +-0.342315 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac7/jem_bay5_agg_pano.txt b/astrobee/behaviors/inspection/resources/isaac7/jem_bay5_agg_pano.txt new file mode 100755 index 00000000..f2289308 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac7/jem_bay5_agg_pano.txt @@ -0,0 +1,29 @@ +10.916 -8.0176 4.94723 -0.073737 0.302644 0.224941 0.92324 +10.8426 -8.04893 4.896 0 0 0.212172 0.977232 +10.8151 -8.06989 4.80751 0.073737 -0.302644 0.224941 0.92324 +10.8776 -8.10424 4.71611 0.2744 -0.524563 0.373565 0.714134 +10.9191 -8.14875 4.80751 0.196571 -0.241641 0.599655 0.737146 +10.9221 -8.11466 4.896 0 0 0.617936 0.786229 +10.9422 -8.03747 4.94723 -0.196571 0.241641 0.599655 0.737146 +10.9821 -7.95374 4.94131 -0.2744 0.524563 0.373565 0.714134 +10.9079 -7.96262 4.94131 -0.553426 0.210194 0.753427 0.286156 +10.9741 -8.02969 4.94723 -0.281299 0.133796 0.858125 0.408156 +11.0229 -8.09278 4.896 0 0 0.899416 0.437093 +11.0459 -8.11786 4.80751 0.281299 -0.133796 0.858125 0.408156 +11.0449 -8.08424 4.71611 0.553426 -0.210194 0.753427 0.286156 +11.1019 -8.00001 4.80751 0.311497 -1.44307e-05 0.950247 4.40219e-05 +11.068 -8.00001 4.896 0 0 1 4.63268e-05 +10.9883 -8 4.94723 -0.311497 1.44307e-05 0.950247 4.40219e-05 +10.9079 -8.03738 4.94131 -0.553445 -0.210143 0.753454 -0.286086 +10.9742 -7.97032 4.94723 -0.281311 -0.13377 0.858163 -0.408076 +11.0229 -7.90724 4.896 0 0 0.899457 -0.43701 +11.0459 -7.88215 4.80751 0.281311 0.13377 0.858163 -0.408076 +11.0449 -7.91578 4.71611 0.553445 0.210143 0.753454 -0.286086 +10.8776 -7.89575 4.71611 -0.274448 -0.524538 -0.373631 0.7141 +10.9191 -7.85124 4.80751 -0.196593 -0.241623 -0.599723 0.73709 +10.9221 -7.88534 4.896 3.53047e-17 -3.5308e-17 -0.618008 0.786171 +10.9422 -7.96253 4.94723 0.196593 0.241623 -0.599723 0.73709 +10.9821 -8.04626 4.94131 0.274448 0.524538 -0.373631 0.7141 +10.916 -7.98239 4.94723 0.073765 0.302637 -0.225026 0.923219 +10.8426 -7.95105 4.896 0 0 -0.212262 0.977213 +10.8151 -7.93009 4.80751 -0.073765 -0.302637 -0.225026 0.923219 diff --git a/astrobee/behaviors/inspection/resources/isaac7/jem_bay6_agg_panorama.txt b/astrobee/behaviors/inspection/resources/isaac7/jem_bay6_agg_panorama.txt new file mode 100755 index 00000000..e8caa668 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac7/jem_bay6_agg_panorama.txt @@ -0,0 +1,29 @@ +10.916 -9.0176 4.94723 -0.073737 0.302644 0.224941 0.92324 +10.8426 -9.04893 4.896 0 0 0.212172 0.977232 +10.8151 -9.06989 4.80751 0.073737 -0.302644 0.224941 0.92324 +10.8776 -9.10424 4.71611 0.2744 -0.524563 0.373565 0.714134 +10.9191 -9.14875 4.80751 0.196571 -0.241641 0.599655 0.737146 +10.9221 -9.11466 4.896 0 0 0.617936 0.786229 +10.9422 -9.03747 4.94723 -0.196571 0.241641 0.599655 0.737146 +10.9821 -8.95374 4.94131 -0.2744 0.524563 0.373565 0.714134 +10.9079 -8.96262 4.94131 -0.553426 0.210194 0.753427 0.286156 +10.9741 -9.02969 4.94723 -0.281299 0.133796 0.858125 0.408156 +11.0229 -9.09278 4.896 0 0 0.899416 0.437093 +11.0459 -9.11786 4.80751 0.281299 -0.133796 0.858125 0.408156 +11.0449 -9.08424 4.71611 0.553426 -0.210194 0.753427 0.286156 +11.1019 -9.00001 4.80751 0.311497 -1.44307e-05 0.950247 4.40219e-05 +11.068 -9.00001 4.896 0 0 1 4.63268e-05 +10.9883 -9 4.94723 -0.311497 1.44307e-05 0.950247 4.40219e-05 +10.9079 -9.03738 4.94131 -0.553445 -0.210143 0.753454 -0.286086 +10.9742 -8.97032 4.94723 -0.281311 -0.13377 0.858163 -0.408076 +11.0229 -8.90724 4.896 0 0 0.899457 -0.43701 +11.0459 -8.88215 4.80751 0.281311 0.13377 0.858163 -0.408076 +11.0449 -8.91578 4.71611 0.553445 0.210143 0.753454 -0.286086 +10.8776 -8.89575 4.71611 -0.274448 -0.524538 -0.373631 0.7141 +10.9191 -8.85124 4.80751 -0.196593 -0.241623 -0.599723 0.73709 +10.9221 -8.88534 4.896 3.53047e-17 -3.5308e-17 -0.618008 0.786171 +10.9422 -8.96253 4.94723 0.196593 0.241623 -0.599723 0.73709 +10.9821 -9.04626 4.94131 0.274448 0.524538 -0.373631 0.7141 +10.916 -8.98239 4.94723 0.073765 0.302637 -0.225026 0.923219 +10.8426 -8.95105 4.896 0 0 -0.212262 0.977213 +10.8151 -8.93009 4.80751 -0.073765 -0.302637 -0.225026 0.923219 diff --git a/astrobee/behaviors/inspection/resources/isaac7/jem_bay7_agg_panorama.txt b/astrobee/behaviors/inspection/resources/isaac7/jem_bay7_agg_panorama.txt new file mode 100755 index 00000000..ef8ee925 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac7/jem_bay7_agg_panorama.txt @@ -0,0 +1,29 @@ +10.916 -10.0176 4.94723 -0.073737 0.302644 0.224941 0.92324 +10.8426 -10.0489 4.896 0 0 0.212172 0.977232 +10.8151 -10.0699 4.80751 0.073737 -0.302644 0.224941 0.92324 +10.8776 -10.1042 4.71611 0.2744 -0.524563 0.373565 0.714134 +10.9191 -10.1488 4.80751 0.196571 -0.241641 0.599655 0.737146 +10.9221 -10.1147 4.896 0 0 0.617936 0.786229 +10.9422 -10.0375 4.94723 -0.196571 0.241641 0.599655 0.737146 +10.9821 -9.95374 4.94131 -0.2744 0.524563 0.373565 0.714134 +10.9079 -9.96262 4.94131 -0.553426 0.210194 0.753427 0.286156 +10.9741 -10.0297 4.94723 -0.281299 0.133796 0.858125 0.408156 +11.0229 -10.0928 4.896 0 0 0.899416 0.437093 +11.0459 -10.1179 4.80751 0.281299 -0.133796 0.858125 0.408156 +11.0449 -10.0842 4.71611 0.553426 -0.210194 0.753427 0.286156 +11.1019 -10 4.80751 0.311497 -1.44307e-05 0.950247 4.40219e-05 +11.068 -10 4.896 0 0 1 4.63268e-05 +10.9883 -10 4.94723 -0.311497 1.44307e-05 0.950247 4.40219e-05 +10.9079 -10.0374 4.94131 -0.553445 -0.210143 0.753454 -0.286086 +10.9742 -9.97032 4.94723 -0.281311 -0.13377 0.858163 -0.408076 +11.0229 -9.90724 4.896 0 0 0.899457 -0.43701 +11.0459 -9.88215 4.80751 0.281311 0.13377 0.858163 -0.408076 +11.0449 -9.91578 4.71611 0.553445 0.210143 0.753454 -0.286086 +10.8776 -9.89575 4.71611 -0.274448 -0.524538 -0.373631 0.7141 +10.9191 -9.85124 4.80751 -0.196593 -0.241623 -0.599723 0.73709 +10.9221 -9.88534 4.896 3.53047e-17 -3.5308e-17 -0.618008 0.786171 +10.9422 -9.96253 4.94723 0.196593 0.241623 -0.599723 0.73709 +10.9821 -10.0463 4.94131 0.274448 0.524538 -0.373631 0.7141 +10.916 -9.98239 4.94723 0.073765 0.302637 -0.225026 0.923219 +10.8426 -9.95105 4.896 0 0 -0.212262 0.977213 +10.8151 -9.93009 4.80751 -0.073765 -0.302637 -0.225026 0.923219 diff --git a/astrobee/behaviors/inspection/resources/isaac8/soundsee_survey_bay5_aft.txt b/astrobee/behaviors/inspection/resources/isaac8/soundsee_survey_bay5_aft.txt new file mode 100644 index 00000000..dfa59d42 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac8/soundsee_survey_bay5_aft.txt @@ -0,0 +1,55 @@ +# Scicam stations +11.019643 -8.20 4.28 0.00 0.00 180.00 +11.019643 -7.70 4.28 0.00 0.00 180.00 +11.019643 -7.70 4.78 0.00 0.00 180.00 +11.019643 -8.20 4.78 0.00 0.00 180.00 +11.019643 -8.20 5.28 0.00 0.00 180.00 +11.019643 -7.70 5.28 0.00 0.00 180.00 +# Soundsee stations +10.38 -8.20 4.08 0.00 0.00 180.00 +10.38 -8.05 4.08 0.00 0.00 180.00 +10.38 -7.90 4.08 0.00 0.00 180.00 +10.38 -7.75 4.08 0.00 0.00 180.00 +10.38 -7.60 4.08 0.00 0.00 180.00 + +10.38 -7.60 4.22 0.00 0.00 180.00 +10.38 -7.75 4.22 0.00 0.00 180.00 +10.38 -7.90 4.22 0.00 0.00 180.00 +10.38 -8.05 4.22 0.00 0.00 180.00 +10.38 -8.20 4.22 0.00 0.00 180.00 + +10.38 -8.20 4.38 0.00 0.00 180.00 +10.38 -8.05 4.38 0.00 0.00 180.00 +10.38 -7.90 4.38 0.00 0.00 180.00 +10.38 -7.75 4.38 0.00 0.00 180.00 +10.38 -7.60 4.38 0.00 0.00 180.00 + +10.38 -7.60 4.52 0.00 0.00 180.00 +10.38 -7.75 4.52 0.00 0.00 180.00 +10.38 -7.90 4.52 0.00 0.00 180.00 +10.38 -8.05 4.52 0.00 0.00 180.00 +10.38 -8.20 4.52 0.00 0.00 180.00 + +10.38 -8.20 4.68 0.00 0.00 180.00 +10.38 -8.05 4.68 0.00 0.00 180.00 +10.38 -7.90 4.68 0.00 0.00 180.00 +10.38 -7.75 4.68 0.00 0.00 180.00 +10.38 -7.60 4.68 0.00 0.00 180.00 + +10.38 -7.60 4.82 0.00 0.00 180.00 +10.38 -7.75 4.82 0.00 0.00 180.00 +10.38 -7.90 4.82 0.00 0.00 180.00 +10.38 -8.05 4.82 0.00 0.00 180.00 +10.38 -8.20 4.82 0.00 0.00 180.00 + +10.38 -8.20 4.98 0.00 0.00 180.00 +10.38 -8.05 4.98 0.00 0.00 180.00 +10.38 -7.90 4.98 0.00 0.00 180.00 +10.38 -7.75 4.98 0.00 0.00 180.00 +10.38 -7.60 4.98 0.00 0.00 180.00 + +10.38 -7.60 5.12 0.00 0.00 180.00 +10.38 -7.75 5.12 0.00 0.00 180.00 +10.38 -7.90 5.12 0.00 0.00 180.00 +10.38 -8.05 5.12 0.00 0.00 180.00 +10.38 -8.20 5.12 0.00 0.00 180.00 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/isaac8/soundsee_survey_bay6_fwd.txt b/astrobee/behaviors/inspection/resources/isaac8/soundsee_survey_bay6_fwd.txt new file mode 100644 index 00000000..ea1ebb79 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac8/soundsee_survey_bay6_fwd.txt @@ -0,0 +1,55 @@ +# Scicam stations +10.84 -9.20 4.28 0.00 0.00 0.00 +10.84 -8.70 4.28 0.00 0.00 0.00 +10.84 -8.70 4.78 0.00 0.00 0.00 +10.84 -9.20 4.78 0.00 0.00 0.00 +10.84 -9.20 5.28 0.00 0.00 0.00 +10.84 -8.70 5.28 0.00 0.00 0.00 +# Soundsee stations +11.48 -9.30 4.08 0.00 0.00 0.00 +11.48 -9.15 4.08 0.00 0.00 0.00 +11.48 -9.00 4.08 0.00 0.00 0.00 +11.48 -8.85 4.08 0.00 0.00 0.00 +11.48 -8.70 4.08 0.00 0.00 0.00 + +11.48 -8.70 4.22 0.00 0.00 0.00 +11.48 -8.85 4.22 0.00 0.00 0.00 +11.48 -9.00 4.22 0.00 0.00 0.00 +11.48 -9.15 4.22 0.00 0.00 0.00 +11.48 -9.30 4.22 0.00 0.00 0.00 + +11.48 -9.30 4.38 0.00 0.00 0.00 +11.48 -9.15 4.38 0.00 0.00 0.00 +11.48 -9.00 4.38 0.00 0.00 0.00 +11.48 -8.85 4.38 0.00 0.00 0.00 +11.48 -8.70 4.38 0.00 0.00 0.00 + +11.48 -8.70 4.52 0.00 0.00 0.00 +11.48 -8.85 4.52 0.00 0.00 0.00 +11.48 -9.00 4.52 0.00 0.00 0.00 +11.48 -9.15 4.52 0.00 0.00 0.00 +11.48 -9.30 4.52 0.00 0.00 0.00 + +11.48 -9.30 4.68 0.00 0.00 0.00 +11.48 -9.15 4.68 0.00 0.00 0.00 +11.48 -9.00 4.68 0.00 0.00 0.00 +11.48 -8.85 4.68 0.00 0.00 0.00 +11.48 -8.70 4.68 0.00 0.00 0.00 + +11.48 -8.70 4.82 0.00 0.00 0.00 +11.48 -8.85 4.82 0.00 0.00 0.00 +11.48 -9.00 4.82 0.00 0.00 0.00 +11.48 -9.15 4.82 0.00 0.00 0.00 +11.48 -9.30 4.82 0.00 0.00 0.00 + +11.48 -9.30 4.98 0.00 0.00 0.00 +11.48 -9.15 4.98 0.00 0.00 0.00 +11.48 -9.00 4.98 0.00 0.00 0.00 +11.48 -8.85 4.98 0.00 0.00 0.00 +11.48 -8.70 4.98 0.00 0.00 0.00 + +11.48 -8.70 5.12 0.00 0.00 0.00 +11.48 -8.85 5.12 0.00 0.00 0.00 +11.48 -9.00 5.12 0.00 0.00 0.00 +11.48 -9.15 5.12 0.00 0.00 0.00 +11.48 -9.30 5.12 0.00 0.00 0.00 diff --git a/astrobee/behaviors/inspection/resources/isaac9/inspection_hatch.txt b/astrobee/behaviors/inspection/resources/isaac9/inspection_hatch.txt new file mode 100644 index 00000000..336946bd --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/inspection_hatch.txt @@ -0,0 +1,13 @@ +# x y z roll pitch yaw +11.05 -3 4.95 -90 0 45 +11.05 -3 4.75 -90 0 45 +11.05 -3 4.75 -0.5 0 0.5 0.707 +11.05 -3 4.75 0 45 90 +10.85 -3 4.75 0 45 90 +10.85 -3 4.75 0 0.5 0.707 0.5 +10.85 -3 4.75 90 0 135 +10.85 -3 4.95 90 0 135 +10.85 -3 4.95 0.5 0.707 0.5 0 +10.85 -3 4.95 180 -45 90 +11.05 -3 4.95 180 -45 90 +11.05 -3 4.95 0.707 0.5 0 -0.5 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay1_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay1_std_panorama.txt new file mode 100644 index 00000000..a95d9d2b --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay1_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -3.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -3.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -3.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -3.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -3.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -3.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -3.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -3.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -3.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -3.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -3.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -4.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -4.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -3.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -3.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -3.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -3.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -3.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -3.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -3.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -3.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -3.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -3.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -3.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -4 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -3.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -4 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -4.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -4 4.94269 0 0.581753 0 0.813365 +10.9915 -4.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -4.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -4.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -4.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -4.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -4 4.71935 0 -0.581753 0 0.813365 +10.9406 -4.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -4.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -4.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -4.11768 4.896 0 0 0.680563 0.73269 +10.9842 -4.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -4.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -3.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -3.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -4.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -4.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -4.1021 4.896 0 0 0.866398 0.499355 +11.0627 -4.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -4.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -4.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -4.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -4.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -4.05099 4.896 0 0 0.975141 0.221587 +11.0623 -4.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -4.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay1_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay1_stereo_panorama.txt new file mode 100644 index 00000000..3b7a8ba5 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay1_stereo_panorama.txt @@ -0,0 +1,108 @@ +11.0077 -3.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -4.19509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -3.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -4.16928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -3.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -4.14901 4.896 0 0 0.975141 -0.221587 +11.1315 -3.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -4.13511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -3.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -4.11891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -3.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -4.0946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -3.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -4.0548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -3.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -4.06742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -3.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -4.0979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -3.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -4.13724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -3.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -4.1912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -4.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -4.24308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -4.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -4.24681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -3.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -4.19186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -3.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -4.13239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -3.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -4.08232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -3.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -4.05719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -3.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -4.06568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -3.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -4.08549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -3.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -4.14591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -3.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -4.10884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -3.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -4.1077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -3.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -4.15685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -3.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -4.19672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -4 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -4.2 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -3.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -4.16519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -4 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -4.2 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -4.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -4.23481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -4 4.94269 0 0.581753 0 0.813365 +11.0527 -4.2 4.94269 0 0.581753 0 0.813365 +10.9915 -4.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -4.20328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -4.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -4.24315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -4.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -4.2923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -4.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -4.29116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -4.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -4.25409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -4 4.71935 0 -0.581753 0 0.813365 +10.871 -4.2 4.71935 0 -0.581753 0 0.813365 +10.9406 -4.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -4.31451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -4.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -4.33432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -4.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -4.34281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -4.11768 4.896 0 0 0.680563 0.73269 +10.9913 -4.31768 4.896 0 0 0.680563 0.73269 +10.9842 -4.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -4.26761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -4.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -4.20814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -3.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -4.15319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -3.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -4.15692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -4.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -4.2088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -4.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -4.26276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -4.1021 4.896 0 0 0.866398 0.499355 +11.0592 -4.3021 4.896 0 0 0.866398 0.499355 +11.0627 -4.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -4.33258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -4.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -4.3452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -4.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -4.3054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -4.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -4.28109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -4.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -4.26489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -4.05099 4.896 0 0 0.975141 0.221587 +11.1064 -4.25099 4.896 0 0 0.975141 0.221587 +11.0623 -4.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -4.23072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -4.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -4.20491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay2_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay2_std_panorama.txt new file mode 100644 index 00000000..5f3eb84c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay2_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -4.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -4.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -4.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -4.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -4.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -4.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -4.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -4.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -4.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -4.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -4.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -5.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -5.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -4.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -4.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -4.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -4.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -4.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -4.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -4.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -4.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -4.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -4.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -4.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -5 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -4.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -5 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -5.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -5 4.94269 0 0.581753 0 0.813365 +10.9915 -5.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -5.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -5.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -5.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -5.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -5 4.71935 0 -0.581753 0 0.813365 +10.9406 -5.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -5.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -5.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -5.11768 4.896 0 0 0.680563 0.73269 +10.9842 -5.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -5.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -4.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -4.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -5.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -5.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -5.1021 4.896 0 0 0.866398 0.499355 +11.0627 -5.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -5.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -5.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -5.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -5.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -5.05099 4.896 0 0 0.975141 0.221587 +11.0623 -5.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -5.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay2_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay2_stereo_panorama.txt new file mode 100644 index 00000000..3915e492 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay2_stereo_panorama.txt @@ -0,0 +1,108 @@ +11.0077 -4.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -5.19509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -4.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -5.16928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -4.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -5.14901 4.896 0 0 0.975141 -0.221587 +11.1315 -4.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -5.13511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -4.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -5.11891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -4.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -5.0946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -4.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -5.0548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -4.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -5.06742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -4.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -5.0979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -4.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -5.13724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -4.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -5.1912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -5.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -5.24308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -5.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -5.24681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -4.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -5.19186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -4.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -5.13239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -4.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -5.08232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -4.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -5.05719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -4.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -5.06568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -4.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -5.08549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -4.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -5.14591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -4.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -5.10884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -4.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -5.1077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -4.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -5.15685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -4.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -5.19672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -5 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -5.2 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -4.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -5.16519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -5 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -5.2 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -5.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -5.23481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -5 4.94269 0 0.581753 0 0.813365 +11.0527 -5.2 4.94269 0 0.581753 0 0.813365 +10.9915 -5.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -5.20328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -5.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -5.24315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -5.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -5.2923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -5.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -5.29116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -5.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -5.25409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -5 4.71935 0 -0.581753 0 0.813365 +10.871 -5.2 4.71935 0 -0.581753 0 0.813365 +10.9406 -5.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -5.31451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -5.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -5.33432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -5.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -5.34281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -5.11768 4.896 0 0 0.680563 0.73269 +10.9913 -5.31768 4.896 0 0 0.680563 0.73269 +10.9842 -5.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -5.26761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -5.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -5.20814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -4.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -5.15319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -4.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -5.15692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -5.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -5.2088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -5.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -5.26276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -5.1021 4.896 0 0 0.866398 0.499355 +11.0592 -5.3021 4.896 0 0 0.866398 0.499355 +11.0627 -5.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -5.33258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -5.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -5.3452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -5.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -5.3054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -5.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -5.28109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -5.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -5.26489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -5.05099 4.896 0 0 0.975141 0.221587 +11.1064 -5.25099 4.896 0 0 0.975141 0.221587 +11.0623 -5.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -5.23072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -5.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -5.20491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay3_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay3_std_panorama.txt new file mode 100644 index 00000000..357e4b8c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay3_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -5.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -5.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -5.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -5.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -5.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -5.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -5.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -5.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -5.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -5.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -5.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -6.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -6.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -5.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -5.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -5.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -5.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -5.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -5.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -5.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -5.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -5.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -5.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -5.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -6 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -5.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -6 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -6.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -6 4.94269 0 0.581753 0 0.813365 +10.9915 -6.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -6.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -6.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -6.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -6.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -6 4.71935 0 -0.581753 0 0.813365 +10.9406 -6.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -6.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -6.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -6.11768 4.896 0 0 0.680563 0.73269 +10.9842 -6.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -6.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -5.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -5.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -6.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -6.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -6.1021 4.896 0 0 0.866398 0.499355 +11.0627 -6.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -6.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -6.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -6.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -6.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -6.05099 4.896 0 0 0.975141 0.221587 +11.0623 -6.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -6.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay3_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay3_stereo_panorama.txt new file mode 100644 index 00000000..9f08ea75 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay3_stereo_panorama.txt @@ -0,0 +1,108 @@ +11.0077 -5.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -6.19509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -5.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -6.16928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -5.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -6.14901 4.896 0 0 0.975141 -0.221587 +11.1315 -5.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -6.13511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -5.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -6.11891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -5.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -6.0946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -5.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -6.0548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -5.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -6.06742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -5.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -6.0979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -5.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -6.13724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -5.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -6.1912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -6.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -6.24308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -6.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -6.24681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -5.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -6.19186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -5.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -6.13239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -5.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -6.08232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -5.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -6.05719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -5.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -6.06568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -5.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -6.08549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -5.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -6.14591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -5.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -6.10884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -5.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -6.1077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -5.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -6.15685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -5.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -6.19672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -6 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -6.2 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -5.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -6.16519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -6 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -6.2 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -6.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -6.23481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -6 4.94269 0 0.581753 0 0.813365 +11.0527 -6.2 4.94269 0 0.581753 0 0.813365 +10.9915 -6.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -6.20328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -6.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -6.24315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -6.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -6.2923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -6.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -6.29116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -6.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -6.25409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -6 4.71935 0 -0.581753 0 0.813365 +10.871 -6.2 4.71935 0 -0.581753 0 0.813365 +10.9406 -6.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -6.31451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -6.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -6.33432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -6.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -6.34281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -6.11768 4.896 0 0 0.680563 0.73269 +10.9913 -6.31768 4.896 0 0 0.680563 0.73269 +10.9842 -6.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -6.26761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -6.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -6.20814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -5.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -6.15319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -5.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -6.15692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -6.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -6.2088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -6.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -6.26276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -6.1021 4.896 0 0 0.866398 0.499355 +11.0592 -6.3021 4.896 0 0 0.866398 0.499355 +11.0627 -6.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -6.33258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -6.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -6.3452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -6.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -6.3054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -6.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -6.28109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -6.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -6.26489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -6.05099 4.896 0 0 0.975141 0.221587 +11.1064 -6.25099 4.896 0 0 0.975141 0.221587 +11.0623 -6.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -6.23072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -6.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -6.20491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay4_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay4_std_panorama.txt new file mode 100644 index 00000000..59bcc180 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay4_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -6.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -6.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -6.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -6.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -6.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -6.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -6.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -6.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -6.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -6.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -6.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -7.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -7.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -6.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -6.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -6.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -6.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -6.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -6.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -6.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -6.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -6.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -6.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -6.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -7 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -6.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -7 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -7.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -7 4.94269 0 0.581753 0 0.813365 +10.9915 -7.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -7.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -7.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -7.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -7.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -7 4.71935 0 -0.581753 0 0.813365 +10.9406 -7.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -7.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -7.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -7.11768 4.896 0 0 0.680563 0.73269 +10.9842 -7.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -7.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -6.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -6.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -7.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -7.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -7.1021 4.896 0 0 0.866398 0.499355 +11.0627 -7.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -7.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -7.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -7.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -7.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -7.05099 4.896 0 0 0.975141 0.221587 +11.0623 -7.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -7.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay4_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay4_stereo_panorama.txt new file mode 100644 index 00000000..f5e3b422 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay4_stereo_panorama.txt @@ -0,0 +1,108 @@ +11.0077 -6.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -6.79509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -6.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -6.76928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -6.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -6.74901 4.896 0 0 0.975141 -0.221587 +11.1315 -6.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -6.73511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -6.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -6.71891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -6.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -6.6946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -6.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -6.6548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -6.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -6.66742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -6.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -6.6979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -6.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -6.73724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -6.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -6.7912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -7.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -6.84308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -7.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -6.84681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -6.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -6.79186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -6.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -6.73239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -6.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -6.68232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -6.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -6.65719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -6.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -6.66568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -6.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -6.68549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -6.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -6.74591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -6.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -6.70884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -6.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -6.7077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -6.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -6.75685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -6.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -6.79672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -7 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -6.8 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -6.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -6.76519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -7 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -6.8 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -7.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -6.83481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -7 4.94269 0 0.581753 0 0.813365 +11.0527 -6.8 4.94269 0 0.581753 0 0.813365 +10.9915 -7.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -6.80328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -7.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -6.84315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -7.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -6.8923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -7.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -6.89116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -7.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -6.85409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -7 4.71935 0 -0.581753 0 0.813365 +10.871 -6.8 4.71935 0 -0.581753 0 0.813365 +10.9406 -7.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -6.91451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -7.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -6.93432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -7.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -6.94281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -7.11768 4.896 0 0 0.680563 0.73269 +10.9913 -6.91768 4.896 0 0 0.680563 0.73269 +10.9842 -7.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -6.86761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -7.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -6.80814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -6.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -6.75319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -6.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -6.75692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -7.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -6.8088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -7.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -6.86276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -7.1021 4.896 0 0 0.866398 0.499355 +11.0592 -6.9021 4.896 0 0 0.866398 0.499355 +11.0627 -7.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -6.93258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -7.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -6.9452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -7.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -6.9054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -7.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -6.88109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -7.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -6.86489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -7.05099 4.896 0 0 0.975141 0.221587 +11.1064 -6.85099 4.896 0 0 0.975141 0.221587 +11.0623 -7.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -6.83072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -7.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -6.80491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/isaac6/jem_bay5_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay5_std_panorama.txt similarity index 100% rename from astrobee/behaviors/inspection/resources/isaac6/jem_bay5_std_panorama.txt rename to astrobee/behaviors/inspection/resources/isaac9/jem_bay5_std_panorama.txt diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay5_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay5_stereo_panorama.txt new file mode 100644 index 00000000..f4ee95d4 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay5_stereo_panorama.txt @@ -0,0 +1,108 @@ +10.9423 -8.00491 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.9423 -7.80491 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -8.03071 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8877 -7.83071 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -8.05098 4.896 0 0 0.221542 0.975151 +10.8436 -7.85098 4.896 0 0 0.221542 0.975151 +10.8185 -8.06488 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8185 -7.86488 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -8.08107 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8232 -7.88107 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -8.10539 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.8757 -7.90539 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -8.14519 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.9103 -7.94519 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -8.13257 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8873 -7.93257 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -8.1021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.8908 -7.9021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -8.06276 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9203 -7.86276 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -8.0088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9476 -7.8088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -7.95692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9804 -7.75692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -7.95319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9257 -7.75319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -8.00814 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9541 -7.80814 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -8.06761 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9658 -7.86761 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -8.11768 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9587 -7.91768 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -8.14281 4.84043 0.160957 -0.12774 0.766581 0.608382 +10.9833 -7.94281 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -8.13433 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0179 -7.93433 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -8.11451 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0094 -7.91451 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -8.0541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0905 -7.8541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -8.09117 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0649 -7.89117 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -8.09231 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0235 -7.89231 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -8.04316 4.93535 -0.194033 0.0676442 0.924113 0.322165 +11.0044 -7.84316 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -8.00328 4.95184 -0.395428 0.0735139 0.900127 0.167343 +10.9585 -7.80328 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -8.00001 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0194 -7.80001 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -8.03482 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0627 -7.83482 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -8.00001 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0966 -7.80001 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -7.96521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +11.0628 -7.76521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -8 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.8973 -7.8 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -7.99672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +10.9585 -7.79672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -7.95685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0044 -7.75685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -7.9077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0235 -7.7077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -7.90885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0649 -7.70885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -7.94593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.0905 -7.74593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -8.00001 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.079 -7.80001 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -7.8855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0094 -7.6855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -7.86568 4.77804 0.342625 0.210654 0.779931 -0.479521 +11.0179 -7.66568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -7.85719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9833 -7.65719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -7.88232 4.896 0 0 -0.732721 0.680529 +10.9587 -7.68232 4.896 0 0 -0.732721 0.680529 +10.9658 -7.93239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9658 -7.73239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -7.99186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9541 -7.79186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -8.0468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9257 -7.8468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -8.04309 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9804 -7.84309 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -7.9912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9476 -7.7912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -7.93723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.9203 -7.73723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -7.89789 4.896 0 0 -0.499395 0.866374 +10.8909 -7.69789 4.896 0 0 -0.499395 0.866374 +10.8873 -7.86742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.8873 -7.66742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -7.8548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.9103 -7.6548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -7.89459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8757 -7.69459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -7.9189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8232 -7.7189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -7.9351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8185 -7.7351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -7.949 4.896 0 0 -0.221632 0.97513 +10.8436 -7.749 4.896 0 0 -0.221632 0.97513 +10.8877 -7.96927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.8877 -7.76927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -7.99509 4.95184 0.112885 0.386037 -0.256964 0.878751 +10.9423 -7.79509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/isaac6/jem_bay6_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay6_std_panorama.txt similarity index 100% rename from astrobee/behaviors/inspection/resources/isaac6/jem_bay6_std_panorama.txt rename to astrobee/behaviors/inspection/resources/isaac9/jem_bay6_std_panorama.txt diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay6_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay6_stereo_panorama.txt new file mode 100644 index 00000000..d3c3f6b4 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay6_stereo_panorama.txt @@ -0,0 +1,108 @@ +10.9423 -9.00491 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.9423 -8.80491 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -9.03071 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8877 -8.83071 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -9.05098 4.896 0 0 0.221542 0.975151 +10.8436 -8.85098 4.896 0 0 0.221542 0.975151 +10.8185 -9.06488 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8185 -8.86488 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -9.08107 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8232 -8.88107 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -9.10539 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.8757 -8.90539 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -9.14519 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.9103 -8.94519 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -9.13257 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8873 -8.93257 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -9.1021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.8908 -8.9021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -9.06276 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9203 -8.86276 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -9.0088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9476 -8.8088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -8.95692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9804 -8.75692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -8.95319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9257 -8.75319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -9.00814 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9541 -8.80814 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -9.06761 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9658 -8.86761 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -9.11768 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9587 -8.91768 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -9.14281 4.84043 0.160957 -0.12774 0.766581 0.608382 +10.9833 -8.94281 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -9.13433 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0179 -8.93433 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -9.11451 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0094 -8.91451 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -9.0541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0905 -8.8541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -9.09117 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0649 -8.89117 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -9.09231 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0235 -8.89231 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -9.04316 4.93535 -0.194033 0.0676442 0.924113 0.322165 +11.0044 -8.84316 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -9.00328 4.95184 -0.395428 0.0735139 0.900127 0.167343 +10.9585 -8.80328 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -9.00001 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0194 -8.80001 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -9.03482 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0627 -8.83482 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -9.00001 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0966 -8.80001 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -8.96521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +11.0628 -8.76521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -9 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.8973 -8.8 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -8.99672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +10.9585 -8.79672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -8.95685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0044 -8.75685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -8.9077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0235 -8.7077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -8.90885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0649 -8.70885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -8.94593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.0905 -8.74593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -9.00001 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.079 -8.80001 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -8.8855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0094 -8.6855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -8.86568 4.77804 0.342625 0.210654 0.779931 -0.479521 +11.0179 -8.66568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -8.85719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9833 -8.65719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -8.88232 4.896 0 0 -0.732721 0.680529 +10.9587 -8.68232 4.896 0 0 -0.732721 0.680529 +10.9658 -8.93239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9658 -8.73239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -8.99186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9541 -8.79186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -9.0468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9257 -8.8468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -9.04309 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9804 -8.84309 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -8.9912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9476 -8.7912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -8.93723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.9203 -8.73723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -8.89789 4.896 0 0 -0.499395 0.866374 +10.8909 -8.69789 4.896 0 0 -0.499395 0.866374 +10.8873 -8.86742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.8873 -8.66742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -8.8548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.9103 -8.6548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -8.89459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8757 -8.69459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -8.9189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8232 -8.7189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -8.9351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8185 -8.7351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -8.949 4.896 0 0 -0.221632 0.97513 +10.8436 -8.749 4.896 0 0 -0.221632 0.97513 +10.8877 -8.96927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.8877 -8.76927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -8.99509 4.95184 0.112885 0.386037 -0.256964 0.878751 +10.9423 -8.79509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_safe_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_safe_panorama.txt new file mode 100644 index 00000000..7ffec970 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_safe_panorama.txt @@ -0,0 +1,54 @@ +10.9423 -9.7049 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -9.7307 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -9.751 4.896 0 0 0.221542 0.975151 +10.8185 -9.7649 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -9.7811 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -9.8054 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -9.8452 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -9.8326 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -9.8021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -9.7628 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -9.7088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -9.65692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -9.65319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -9.7081 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -9.7676 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -9.8177 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -9.8428 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -9.8343 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -9.8145 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -9.7541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -9.7912 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -9.7923 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -9.7432 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -9.7033 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -9.7 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -9.7348 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -9.7 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -9.66521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -9.7 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -9.69672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -9.65685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -9.6077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -9.60885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -9.64593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -9.7 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -9.5855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -9.56568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -9.55719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -9.58232 4.896 0 0 -0.732721 0.680529 +10.9658 -9.63239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -9.69186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -9.7468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -9.7431 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -9.6912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -9.63723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -9.59789 4.896 0 0 -0.499395 0.866374 +10.8873 -9.56742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -9.5548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -9.59459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -9.6189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -9.6351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -9.649 4.896 0 0 -0.221632 0.97513 +10.8877 -9.66927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -9.69509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/isaac6/jem_bay7_std_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_std_panorama.txt similarity index 100% rename from astrobee/behaviors/inspection/resources/isaac6/jem_bay7_std_panorama.txt rename to astrobee/behaviors/inspection/resources/isaac9/jem_bay7_std_panorama.txt diff --git a/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_stereo_panorama.txt b/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_stereo_panorama.txt new file mode 100644 index 00000000..b764e5df --- /dev/null +++ b/astrobee/behaviors/inspection/resources/isaac9/jem_bay7_stereo_panorama.txt @@ -0,0 +1,108 @@ +10.9423 -10.0049 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.9423 -9.8049 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -10.0307 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8877 -9.8307 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -10.051 4.896 0 0 0.221542 0.975151 +10.8436 -9.851 4.896 0 0 0.221542 0.975151 +10.8185 -10.0649 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8185 -9.8649 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -10.0811 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8232 -9.8811 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -10.1054 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.8757 -9.9054 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -10.1452 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.9103 -9.9452 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -10.1326 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8873 -9.9326 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -10.1021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.8908 -9.9021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -10.0628 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9203 -9.8628 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -10.0088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9476 -9.8088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -9.95692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9804 -9.75692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -9.95319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9257 -9.75319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -10.0081 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9541 -9.8081 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -10.0676 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9658 -9.8676 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -10.1177 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9587 -9.9177 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -10.1428 4.84043 0.160957 -0.12774 0.766581 0.608382 +10.9833 -9.9428 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -10.1343 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0179 -9.9343 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -10.1145 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0094 -9.9145 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -10.0541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0905 -9.8541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -10.0912 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0649 -9.8912 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -10.0923 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0235 -9.8923 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -10.0432 4.93535 -0.194033 0.0676442 0.924113 0.322165 +11.0044 -9.8432 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -10.0033 4.95184 -0.395428 0.0735139 0.900127 0.167343 +10.9585 -9.8033 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -10 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0194 -9.8 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -10.0348 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0627 -9.8348 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -10 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0966 -9.8 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -9.96521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +11.0628 -9.76521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -10 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.8973 -9.8 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -9.99672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +10.9585 -9.79672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -9.95685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0044 -9.75685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -9.9077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0235 -9.7077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -9.90885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0649 -9.70885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -9.94593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.0905 -9.74593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -10 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.079 -9.8 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -9.8855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0094 -9.6855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -9.86568 4.77804 0.342625 0.210654 0.779931 -0.479521 +11.0179 -9.66568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -9.85719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9833 -9.65719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -9.88232 4.896 0 0 -0.732721 0.680529 +10.9587 -9.68232 4.896 0 0 -0.732721 0.680529 +10.9658 -9.93239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9658 -9.73239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -9.99186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9541 -9.79186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -10.0468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9257 -9.8468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -10.0431 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9804 -9.8431 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -9.9912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9476 -9.7912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -9.93723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.9203 -9.73723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -9.89789 4.896 0 0 -0.499395 0.866374 +10.8909 -9.69789 4.896 0 0 -0.499395 0.866374 +10.8873 -9.86742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.8873 -9.66742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -9.8548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.9103 -9.6548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -9.89459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8757 -9.69459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -9.9189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8232 -9.7189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -9.9351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8185 -9.7351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -9.949 4.896 0 0 -0.221632 0.97513 +10.8436 -9.749 4.896 0 0 -0.221632 0.97513 +10.8877 -9.96927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.8877 -9.76927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -9.99509 4.95184 0.112885 0.386037 -0.256964 0.878751 +10.9423 -9.79509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/pano_test_cases.csv b/astrobee/behaviors/inspection/resources/pano_test_cases.csv new file mode 100644 index 00000000..dab56831 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/pano_test_cases.csv @@ -0,0 +1,8 @@ +"label","pan_radius_degrees","tilt_radius_degrees","h_fov_degrees","v_fov_degrees","overlap","plan_attitude_tolerance_degrees","test_attitude_tolerance_degrees" +"0_spherical",180,90,59.0,44.8,0.3,5,5 +"1_directional",60,30,59.0,44.8,0.3,7,5 +"2_one_row",60,5,60,60,0.3,7,5 +"3_one_column",0,60,60,60,0.3,7,5 +"4_mapper",180,91.45,59.0,47.7,0.0,5,5 +"5_mapper_and_hugin",180,90,61.2,47.7,0.3,5,5 +"6_nav_hugin",180,90,115.9,85.4,0.3,5,5 diff --git a/astrobee/behaviors/inspection/resources/panorama_granite.txt b/astrobee/behaviors/inspection/resources/panorama_granite.txt index af8530ba..981a4f6a 100644 --- a/astrobee/behaviors/inspection/resources/panorama_granite.txt +++ b/astrobee/behaviors/inspection/resources/panorama_granite.txt @@ -1,2 +1,2 @@ # Panorama -0 0 -0.772 0 0 0 \ No newline at end of file +0 0 -0.772 0 0 -90 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/panorama_granite_wannabee.txt b/astrobee/behaviors/inspection/resources/panorama_granite_wannabee.txt new file mode 100644 index 00000000..4bfd7d7c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panorama_granite_wannabee.txt @@ -0,0 +1,2 @@ +# Panorama +0 0 -0.6 180 0 -90 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay1_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay1_std_panorama.txt new file mode 100644 index 00000000..a95d9d2b --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay1_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -3.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -3.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -3.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -3.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -3.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -3.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -3.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -3.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -3.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -3.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -3.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -4.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -4.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -3.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -3.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -3.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -3.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -3.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -3.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -3.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -3.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -3.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -3.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -3.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -4 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -3.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -4 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -4.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -4 4.94269 0 0.581753 0 0.813365 +10.9915 -4.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -4.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -4.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -4.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -4.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -4 4.71935 0 -0.581753 0 0.813365 +10.9406 -4.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -4.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -4.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -4.11768 4.896 0 0 0.680563 0.73269 +10.9842 -4.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -4.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -3.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -3.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -4.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -4.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -4.1021 4.896 0 0 0.866398 0.499355 +11.0627 -4.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -4.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -4.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -4.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -4.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -4.05099 4.896 0 0 0.975141 0.221587 +11.0623 -4.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -4.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay1_std_panorama_stereo.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay1_std_panorama_stereo.txt new file mode 100644 index 00000000..3b7a8ba5 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay1_std_panorama_stereo.txt @@ -0,0 +1,108 @@ +11.0077 -3.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -4.19509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -3.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -4.16928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -3.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -4.14901 4.896 0 0 0.975141 -0.221587 +11.1315 -3.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -4.13511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -3.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -4.11891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -3.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -4.0946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -3.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -4.0548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -3.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -4.06742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -3.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -4.0979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -3.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -4.13724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -3.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -4.1912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -4.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -4.24308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -4.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -4.24681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -3.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -4.19186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -3.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -4.13239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -3.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -4.08232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -3.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -4.05719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -3.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -4.06568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -3.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -4.08549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -3.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -4.14591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -3.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -4.10884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -3.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -4.1077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -3.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -4.15685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -3.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -4.19672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -4 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -4.2 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -3.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -4.16519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -4 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -4.2 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -4.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -4.23481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -4 4.94269 0 0.581753 0 0.813365 +11.0527 -4.2 4.94269 0 0.581753 0 0.813365 +10.9915 -4.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -4.20328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -4.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -4.24315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -4.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -4.2923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -4.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -4.29116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -4.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -4.25409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -4 4.71935 0 -0.581753 0 0.813365 +10.871 -4.2 4.71935 0 -0.581753 0 0.813365 +10.9406 -4.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -4.31451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -4.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -4.33432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -4.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -4.34281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -4.11768 4.896 0 0 0.680563 0.73269 +10.9913 -4.31768 4.896 0 0 0.680563 0.73269 +10.9842 -4.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -4.26761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -4.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -4.20814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -3.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -4.15319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -3.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -4.15692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -4.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -4.2088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -4.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -4.26276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -4.1021 4.896 0 0 0.866398 0.499355 +11.0592 -4.3021 4.896 0 0 0.866398 0.499355 +11.0627 -4.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -4.33258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -4.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -4.3452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -4.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -4.3054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -4.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -4.28109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -4.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -4.26489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -4.05099 4.896 0 0 0.975141 0.221587 +11.1064 -4.25099 4.896 0 0 0.975141 0.221587 +11.0623 -4.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -4.23072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -4.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -4.20491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay2_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay2_std_panorama.txt new file mode 100644 index 00000000..5f3eb84c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay2_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -4.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -4.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -4.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -4.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -4.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -4.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -4.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -4.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -4.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -4.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -4.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -5.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -5.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -4.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -4.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -4.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -4.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -4.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -4.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -4.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -4.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -4.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -4.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -4.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -5 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -4.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -5 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -5.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -5 4.94269 0 0.581753 0 0.813365 +10.9915 -5.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -5.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -5.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -5.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -5.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -5 4.71935 0 -0.581753 0 0.813365 +10.9406 -5.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -5.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -5.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -5.11768 4.896 0 0 0.680563 0.73269 +10.9842 -5.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -5.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -4.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -4.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -5.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -5.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -5.1021 4.896 0 0 0.866398 0.499355 +11.0627 -5.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -5.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -5.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -5.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -5.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -5.05099 4.896 0 0 0.975141 0.221587 +11.0623 -5.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -5.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay2_std_panorama_stereo.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay2_std_panorama_stereo.txt new file mode 100644 index 00000000..3915e492 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay2_std_panorama_stereo.txt @@ -0,0 +1,108 @@ +11.0077 -4.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -5.19509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -4.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -5.16928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -4.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -5.14901 4.896 0 0 0.975141 -0.221587 +11.1315 -4.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -5.13511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -4.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -5.11891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -4.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -5.0946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -4.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -5.0548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -4.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -5.06742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -4.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -5.0979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -4.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -5.13724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -4.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -5.1912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -5.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -5.24308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -5.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -5.24681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -4.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -5.19186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -4.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -5.13239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -4.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -5.08232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -4.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -5.05719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -4.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -5.06568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -4.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -5.08549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -4.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -5.14591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -4.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -5.10884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -4.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -5.1077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -4.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -5.15685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -4.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -5.19672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -5 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -5.2 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -4.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -5.16519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -5 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -5.2 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -5.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -5.23481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -5 4.94269 0 0.581753 0 0.813365 +11.0527 -5.2 4.94269 0 0.581753 0 0.813365 +10.9915 -5.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -5.20328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -5.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -5.24315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -5.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -5.2923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -5.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -5.29116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -5.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -5.25409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -5 4.71935 0 -0.581753 0 0.813365 +10.871 -5.2 4.71935 0 -0.581753 0 0.813365 +10.9406 -5.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -5.31451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -5.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -5.33432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -5.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -5.34281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -5.11768 4.896 0 0 0.680563 0.73269 +10.9913 -5.31768 4.896 0 0 0.680563 0.73269 +10.9842 -5.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -5.26761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -5.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -5.20814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -4.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -5.15319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -4.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -5.15692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -5.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -5.2088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -5.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -5.26276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -5.1021 4.896 0 0 0.866398 0.499355 +11.0592 -5.3021 4.896 0 0 0.866398 0.499355 +11.0627 -5.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -5.33258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -5.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -5.3452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -5.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -5.3054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -5.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -5.28109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -5.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -5.26489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -5.05099 4.896 0 0 0.975141 0.221587 +11.1064 -5.25099 4.896 0 0 0.975141 0.221587 +11.0623 -5.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -5.23072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -5.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -5.20491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay3_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay3_std_panorama.txt new file mode 100644 index 00000000..357e4b8c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay3_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -5.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -5.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -5.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -5.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -5.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -5.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -5.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -5.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -5.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -5.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -5.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -6.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -6.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -5.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -5.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -5.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -5.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -5.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -5.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -5.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -5.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -5.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -5.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -5.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -6 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -5.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -6 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -6.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -6 4.94269 0 0.581753 0 0.813365 +10.9915 -6.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -6.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -6.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -6.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -6.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -6 4.71935 0 -0.581753 0 0.813365 +10.9406 -6.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -6.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -6.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -6.11768 4.896 0 0 0.680563 0.73269 +10.9842 -6.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -6.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -5.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -5.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -6.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -6.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -6.1021 4.896 0 0 0.866398 0.499355 +11.0627 -6.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -6.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -6.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -6.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -6.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -6.05099 4.896 0 0 0.975141 0.221587 +11.0623 -6.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -6.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay3_std_panorama_stereo.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay3_std_panorama_stereo.txt new file mode 100644 index 00000000..9f08ea75 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay3_std_panorama_stereo.txt @@ -0,0 +1,108 @@ +11.0077 -5.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0077 -6.19509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -5.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.0623 -6.16928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -5.94901 4.896 0 0 0.975141 -0.221587 +11.1064 -6.14901 4.896 0 0 0.975141 -0.221587 +11.1315 -5.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1315 -6.13511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -5.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.1268 -6.11891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -5.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0743 -6.0946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -5.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0397 -6.0548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -5.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0627 -6.06742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -5.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0592 -6.0979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -5.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0297 -6.13724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -5.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +11.0024 -6.1912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -6.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.9696 -6.24308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -6.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +11.0243 -6.24681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -5.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9959 -6.19186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -5.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9842 -6.13239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -5.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9913 -6.08232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -5.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9667 -6.05719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -5.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9321 -6.06568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -5.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.9406 -6.08549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -5.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8595 -6.14591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -5.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.8851 -6.10884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -5.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9265 -6.1077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -5.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9456 -6.15685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -5.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9915 -6.19672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -6 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.9306 -6.2 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -5.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8872 -6.16519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -6 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8534 -6.2 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -6.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.8872 -6.23481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -6 4.94269 0 0.581753 0 0.813365 +11.0527 -6.2 4.94269 0 0.581753 0 0.813365 +10.9915 -6.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9915 -6.20328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -6.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9456 -6.24315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -6.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.9265 -6.2923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -6.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8851 -6.29116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -6.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.8595 -6.25409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -6 4.71935 0 -0.581753 0 0.813365 +10.871 -6.2 4.71935 0 -0.581753 0 0.813365 +10.9406 -6.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9406 -6.31451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -6.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9321 -6.33432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -6.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9667 -6.34281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -6.11768 4.896 0 0 0.680563 0.73269 +10.9913 -6.31768 4.896 0 0 0.680563 0.73269 +10.9842 -6.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9842 -6.26761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -6.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.9959 -6.20814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -5.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +11.0243 -6.15319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -5.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.9696 -6.15692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -6.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0024 -6.2088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -6.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0297 -6.26276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -6.1021 4.896 0 0 0.866398 0.499355 +11.0592 -6.3021 4.896 0 0 0.866398 0.499355 +11.0627 -6.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0627 -6.33258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -6.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0397 -6.3452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -6.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.0743 -6.3054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -6.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1268 -6.28109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -6.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1315 -6.26489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -6.05099 4.896 0 0 0.975141 0.221587 +11.1064 -6.25099 4.896 0 0 0.975141 0.221587 +11.0623 -6.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0623 -6.23072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -6.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 +11.0077 -6.20491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay4_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay4_std_panorama.txt new file mode 100644 index 00000000..59bcc180 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay4_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 -6.99509 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 -6.96928 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 -6.94901 4.896 0 0 0.975141 -0.221587 +11.1315 -6.93511 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 -6.91891 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 -6.8946 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 -6.8548 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 -6.86742 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 -6.8979 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 -6.93724 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 -6.9912 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -7.04308 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -7.04681 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 -6.99186 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 -6.93239 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 -6.88232 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 -6.85719 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 -6.86568 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 -6.88549 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 -6.94591 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 -6.90884 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 -6.9077 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 -6.95685 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 -6.99672 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 -7 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 -6.96519 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 -7 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -7.03481 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 -7 4.94269 0 0.581753 0 0.813365 +10.9915 -7.00328 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -7.04315 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -7.0923 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -7.09116 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -7.05409 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 -7 4.71935 0 -0.581753 0 0.813365 +10.9406 -7.11451 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -7.13432 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -7.14281 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -7.11768 4.896 0 0 0.680563 0.73269 +10.9842 -7.06761 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -7.00814 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 -6.95319 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 -6.95692 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -7.0088 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -7.06276 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -7.1021 4.896 0 0 0.866398 0.499355 +11.0627 -7.13258 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -7.1452 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -7.1054 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -7.08109 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -7.06489 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -7.05099 4.896 0 0 0.975141 0.221587 +11.0623 -7.03072 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -7.00491 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay5_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay5_std_panorama.txt new file mode 100755 index 00000000..84a78674 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay5_std_panorama.txt @@ -0,0 +1,54 @@ +10.9423 -8.00491 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -8.03071 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -8.05098 4.896 0 0 0.221542 0.975151 +10.8185 -8.06488 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -8.08107 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -8.10539 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -8.14519 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -8.13257 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -8.1021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -8.06276 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -8.0088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -7.95692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -7.95319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -8.00814 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -8.06761 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -8.11768 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -8.14281 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -8.13433 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -8.11451 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -8.0541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -8.09117 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -8.09231 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -8.04316 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -8.00328 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -8.00001 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -8.03482 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -8.00001 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -7.96521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -8 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -7.99672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -7.95685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -7.9077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -7.90885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -7.94593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -8.00001 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -7.8855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -7.86568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -7.85719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -7.88232 4.896 0 0 -0.732721 0.680529 +10.9658 -7.93239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -7.99186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -8.0468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -8.04309 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -7.9912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -7.93723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -7.89789 4.896 0 0 -0.499395 0.866374 +10.8873 -7.86742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -7.8548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -7.89459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -7.9189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -7.9351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -7.949 4.896 0 0 -0.221632 0.97513 +10.8877 -7.96927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -7.99509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay6_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay6_std_panorama.txt new file mode 100755 index 00000000..ab3664b0 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay6_std_panorama.txt @@ -0,0 +1,54 @@ +10.9423 -9.00491 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -9.03071 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -9.05098 4.896 0 0 0.221542 0.975151 +10.8185 -9.06488 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -9.08107 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -9.10539 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -9.14519 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -9.13257 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -9.1021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -9.06276 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -9.0088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -8.95692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -8.95319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -9.00814 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -9.06761 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -9.11768 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -9.14281 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -9.13433 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -9.11451 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -9.0541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -9.09117 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -9.09231 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -9.04316 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -9.00328 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -9.00001 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -9.03482 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -9.00001 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -8.96521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -9 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -8.99672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -8.95685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -8.9077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -8.90885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -8.94593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -9.00001 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -8.8855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -8.86568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -8.85719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -8.88232 4.896 0 0 -0.732721 0.680529 +10.9658 -8.93239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -8.99186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -9.0468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -9.04309 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -8.9912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -8.93723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -8.89789 4.896 0 0 -0.499395 0.866374 +10.8873 -8.86742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -8.8548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -8.89459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -8.9189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -8.9351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -8.949 4.896 0 0 -0.221632 0.97513 +10.8877 -8.96927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -8.99509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/panoramas/jem_bay7_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/jem_bay7_std_panorama.txt new file mode 100755 index 00000000..8430ef78 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/jem_bay7_std_panorama.txt @@ -0,0 +1,54 @@ +10.9423 -10.0049 4.95184 -0.112849 0.386047 0.256882 0.878774 +10.8877 -10.0307 4.93535 -0.0466766 0.200115 0.222304 0.953077 +10.8436 -10.051 4.896 0 0 0.221542 0.975151 +10.8185 -10.0649 4.84043 0.0466766 -0.200115 0.222304 0.953077 +10.8232 -10.0811 4.77804 0.112849 -0.386047 0.256882 0.878774 +10.8757 -10.1054 4.71935 0.267721 -0.51649 0.374309 0.722119 +10.9103 -10.1452 4.77804 0.244024 -0.319718 0.555481 0.727787 +10.8873 -10.1326 4.84043 0.109943 -0.173601 0.523619 0.8268 +10.8908 -10.1021 4.896 -1.60189e-17 -1.60174e-17 0.499315 0.866421 +10.9203 -10.0628 4.93535 -0.109943 0.173601 0.523619 0.8268 +10.9476 -10.0088 4.95184 -0.244024 0.319718 0.555481 0.727787 +10.9804 -9.95692 4.94269 -0.267721 0.51649 0.374309 0.722119 +10.9257 -9.95319 4.94269 -0.497077 0.302244 0.694977 0.422576 +10.9541 -10.0081 4.95184 -0.342606 0.210686 0.779887 0.479593 +10.9658 -10.0676 4.93535 -0.160957 0.12774 0.766581 0.608382 +10.9587 -10.1177 4.896 5.35266e-17 -2.80409e-17 0.732658 0.680597 +10.9833 -10.1428 4.84043 0.160957 -0.12774 0.766581 0.608382 +11.0179 -10.1343 4.77804 0.342606 -0.210686 0.779887 0.479593 +11.0094 -10.1145 4.71935 0.497077 -0.302244 0.694977 0.422576 +11.0905 -10.0541 4.77804 0.395428 -0.0735139 0.900127 0.167343 +11.0649 -10.0912 4.84043 0.194033 -0.0676442 0.924113 0.322165 +11.0235 -10.0923 4.896 9.24325e-17 -1.54087e-17 0.90081 0.434213 +11.0044 -10.0432 4.93535 -0.194033 0.0676442 0.924113 0.322165 +10.9585 -10.0033 4.95184 -0.395428 0.0735139 0.900127 0.167343 +11.0194 -10 4.93535 -0.205487 9.51953e-06 0.97866 4.53382e-05 +11.0627 -10.0348 4.896 2.80697e-17 -2.80723e-17 0.988808 0.149193 +11.0966 -10 4.84043 0.205487 -9.51953e-06 0.97866 4.53382e-05 +11.0628 -9.96521 4.896 1.40347e-17 -1.4036e-17 0.988822 -0.149102 +10.8973 -10 4.94269 -0.581753 2.69508e-05 0.813365 3.76806e-05 +10.9585 -9.99672 4.95184 -0.395434 -0.0734772 0.900143 -0.167259 +11.0044 -9.95685 4.93535 -0.19404 -0.0676262 0.924143 -0.32208 +11.0235 -9.9077 4.896 3.08104e-17 -3.08133e-17 0.90085 -0.43413 +11.0649 -9.90885 4.84043 0.19404 0.0676262 0.924143 -0.32208 +11.0905 -9.94593 4.77804 0.395434 0.0734772 0.900143 -0.167259 +11.079 -10 4.71935 0.581753 -2.69508e-05 0.813365 3.76806e-05 +11.0094 -9.8855 4.71935 0.497105 0.302198 0.695016 -0.422511 +11.0179 -9.86568 4.77804 0.342625 0.210654 0.779931 -0.479521 +10.9833 -9.85719 4.84043 -0.160969 -0.127725 -0.766637 0.608311 +10.9587 -9.88232 4.896 0 0 -0.732721 0.680529 +10.9658 -9.93239 4.93535 0.160969 0.127725 -0.766637 0.608311 +10.9541 -9.99186 4.95184 -0.342625 -0.210654 0.779931 -0.479521 +10.9257 -10.0468 4.94269 -0.497105 -0.302198 0.695016 -0.422511 +10.9804 -10.0431 4.94269 0.267769 0.516465 -0.374376 0.722084 +10.9476 -9.9912 4.95184 0.244053 0.319696 -0.555548 0.727736 +10.9203 -9.93723 4.93535 0.109959 0.173591 -0.523696 0.826752 +10.8909 -9.89789 4.896 0 0 -0.499395 0.866374 +10.8873 -9.86742 4.84043 -0.109959 -0.173591 -0.523696 0.826752 +10.9103 -9.8548 4.77804 -0.244053 -0.319696 -0.555548 0.727736 +10.8757 -9.89459 4.71935 -0.267769 -0.516465 -0.374376 0.722084 +10.8232 -9.9189 4.77804 -0.112885 -0.386037 -0.256964 0.878751 +10.8185 -9.9351 4.84043 -0.0466951 -0.200111 -0.222392 0.953057 +10.8436 -9.949 4.896 0 0 -0.221632 0.97513 +10.8877 -9.96927 4.93535 0.0466951 0.200111 -0.222392 0.953057 +10.9423 -9.99509 4.95184 0.112885 0.386037 -0.256964 0.878751 diff --git a/astrobee/behaviors/inspection/resources/panoramas/nod2_bay2_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/nod2_bay2_std_panorama.txt new file mode 100644 index 00000000..c460d4f7 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/nod2_bay2_std_panorama.txt @@ -0,0 +1,54 @@ +11.0077 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +11.0623 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +11.1064 0.0509946 4.896 0 0 0.975141 -0.221587 +11.1315 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +11.1268 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +11.0743 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +11.0397 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +11.0627 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +11.0592 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +11.0297 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +11.0024 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +10.9696 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +11.0243 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +10.9959 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +10.9842 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +10.9913 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +10.9667 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +10.9321 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +10.9406 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +10.8595 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +10.8851 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +10.9265 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +10.9456 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +10.9915 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +10.9306 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +10.8872 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +10.8534 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +10.8872 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +11.0527 0 4.94269 0 0.581753 0 0.813365 +10.9915 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +10.9456 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +10.9265 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +10.8851 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +10.8595 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +10.871 0 4.71935 0 -0.581753 0 0.813365 +10.9406 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +10.9321 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +10.9667 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +10.9913 -0.117679 4.896 0 0 0.680563 0.73269 +10.9842 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +10.9959 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +11.0243 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +10.9696 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +11.0024 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +11.0297 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +11.0592 -0.102103 4.896 0 0 0.866398 0.499355 +11.0627 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +11.0397 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +11.0743 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +11.1268 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +11.1315 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +11.1064 -0.0509946 4.896 0 0 0.975141 0.221587 +11.0623 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +11.0077 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/nod2_bay3_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/nod2_bay3_std_panorama.txt new file mode 100644 index 00000000..fa503286 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/nod2_bay3_std_panorama.txt @@ -0,0 +1,54 @@ +10.0077 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +10.0623 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +10.1064 0.0509946 4.896 0 0 0.975141 -0.221587 +10.1315 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +10.1268 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +10.0743 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +10.0397 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +10.0627 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +10.0592 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +10.0297 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +10.0024 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +9.96961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +10.0243 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +9.99588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +9.98423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +9.99131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +9.96668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +9.93207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +9.94064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +9.85953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +9.88513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +9.92649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +9.94562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +9.99149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +9.93058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +9.88725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +9.85335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +9.88725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +10.0527 0 4.94269 0 0.581753 0 0.813365 +9.99149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +9.94562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +9.92649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +9.88513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +9.85953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +9.87102 0 4.71935 0 -0.581753 0 0.813365 +9.94064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +9.93207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +9.96668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +9.99131 -0.117679 4.896 0 0 0.680563 0.73269 +9.98423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +9.99588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +10.0243 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +9.96961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +10.0024 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +10.0297 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +10.0592 -0.102103 4.896 0 0 0.866398 0.499355 +10.0627 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +10.0397 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +10.0743 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +10.1268 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +10.1315 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +10.1064 -0.0509946 4.896 0 0 0.975141 0.221587 +10.0623 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +10.0077 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/nod2_bay4_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/nod2_bay4_std_panorama.txt new file mode 100644 index 00000000..03a84464 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/nod2_bay4_std_panorama.txt @@ -0,0 +1,54 @@ +9.00768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +9.06226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +9.10641 0.0509946 4.896 0 0 0.975141 -0.221587 +9.13151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +9.12682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +9.07434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +9.03969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +9.06267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +9.05915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +9.02967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +9.00241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +8.96961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +9.02426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +8.99588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +8.98423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +8.99131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +8.96668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +8.93207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +8.94064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +8.85953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +8.88513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +8.92649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +8.94562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +8.99149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +8.93058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +8.88725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +8.85335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +8.88725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +9.05272 0 4.94269 0 0.581753 0 0.813365 +8.99149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +8.94562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +8.92649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +8.88513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +8.85953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +8.87102 0 4.71935 0 -0.581753 0 0.813365 +8.94064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +8.93207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +8.96668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +8.99131 -0.117679 4.896 0 0 0.680563 0.73269 +8.98423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +8.99588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +9.02426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +8.96961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +9.00241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +9.02967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +9.05915 -0.102103 4.896 0 0 0.866398 0.499355 +9.06267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +9.03969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +9.07434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +9.12682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +9.13151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +9.10641 -0.0509946 4.896 0 0 0.975141 0.221587 +9.06226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +9.00768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/usl_bay1_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/usl_bay1_std_panorama.txt new file mode 100644 index 00000000..31cbd5ed --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/usl_bay1_std_panorama.txt @@ -0,0 +1,54 @@ +4.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +4.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +4.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +4.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +4.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +4.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +4.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +4.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +4.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +4.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +4.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +4.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +4.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +4.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +4.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +4.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +4.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +4.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +4.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +4.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +4.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +4.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +4.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +4.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +4.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +4.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +4.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +4.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +4.70272 0 4.94269 0 0.581753 0 0.813365 +4.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +4.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +4.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +4.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +4.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +4.52102 0 4.71935 0 -0.581753 0 0.813365 +4.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +4.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +4.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +4.64131 -0.117679 4.896 0 0 0.680563 0.73269 +4.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +4.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +4.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +4.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +4.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +4.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +4.70915 -0.102103 4.896 0 0 0.866398 0.499355 +4.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +4.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +4.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +4.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +4.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +4.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +4.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +4.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/usl_bay2_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/usl_bay2_std_panorama.txt new file mode 100644 index 00000000..363d59b5 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/usl_bay2_std_panorama.txt @@ -0,0 +1,54 @@ +3.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +3.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +3.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +3.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +3.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +3.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +3.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +3.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +3.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +3.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +3.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +3.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +3.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +3.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +3.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +3.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +3.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +3.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +3.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +3.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +3.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +3.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +3.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +3.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +3.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +3.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +3.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +3.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +3.70272 0 4.94269 0 0.581753 0 0.813365 +3.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +3.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +3.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +3.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +3.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +3.52102 0 4.71935 0 -0.581753 0 0.813365 +3.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +3.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +3.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +3.64131 -0.117679 4.896 0 0 0.680563 0.73269 +3.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +3.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +3.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +3.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +3.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +3.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +3.70915 -0.102103 4.896 0 0 0.866398 0.499355 +3.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +3.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +3.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +3.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +3.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +3.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +3.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +3.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/usl_bay3_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/usl_bay3_std_panorama.txt new file mode 100644 index 00000000..72eb45a2 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/usl_bay3_std_panorama.txt @@ -0,0 +1,54 @@ +2.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +2.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +2.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +2.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +2.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +2.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +2.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +2.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +2.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +2.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +2.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +2.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +2.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +2.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +2.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +2.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +2.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +2.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +2.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +2.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +2.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +2.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +2.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +2.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +2.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +2.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +2.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +2.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +2.70272 0 4.94269 0 0.581753 0 0.813365 +2.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +2.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +2.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +2.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +2.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +2.52102 0 4.71935 0 -0.581753 0 0.813365 +2.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +2.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +2.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +2.64131 -0.117679 4.896 0 0 0.680563 0.73269 +2.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +2.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +2.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +2.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +2.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +2.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +2.70915 -0.102103 4.896 0 0 0.866398 0.499355 +2.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +2.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +2.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +2.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +2.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +2.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +2.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +2.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/usl_bay4_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/usl_bay4_std_panorama.txt new file mode 100644 index 00000000..1c1ffc57 --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/usl_bay4_std_panorama.txt @@ -0,0 +1,54 @@ +1.65768 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +1.71226 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +1.75641 0.0509946 4.896 0 0 0.975141 -0.221587 +1.78151 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +1.77682 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +1.72434 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +1.68969 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +1.71267 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +1.70915 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +1.67967 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +1.65241 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +1.61961 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +1.67426 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +1.64588 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +1.63423 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +1.64131 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +1.61668 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +1.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +1.59064 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +1.50953 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +1.53513 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +1.57649 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +1.59562 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +1.64149 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +1.58058 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +1.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +1.50335 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +1.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +1.70272 0 4.94269 0 0.581753 0 0.813365 +1.64149 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +1.59562 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +1.57649 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +1.53513 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +1.50953 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +1.52102 0 4.71935 0 -0.581753 0 0.813365 +1.59064 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +1.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +1.61668 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +1.64131 -0.117679 4.896 0 0 0.680563 0.73269 +1.63423 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +1.64588 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +1.67426 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +1.61961 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +1.65241 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +1.67967 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +1.70915 -0.102103 4.896 0 0 0.866398 0.499355 +1.71267 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +1.68969 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +1.72434 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +1.77682 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +1.78151 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +1.75641 -0.0509946 4.896 0 0 0.975141 0.221587 +1.71226 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +1.65768 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/usl_bay5_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/usl_bay5_std_panorama.txt new file mode 100644 index 00000000..e635385d --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/usl_bay5_std_panorama.txt @@ -0,0 +1,54 @@ +0.657685 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +0.712256 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +0.756412 0.0509946 4.896 0 0 0.975141 -0.221587 +0.781507 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +0.776817 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +0.724338 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +0.689693 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +0.712675 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +0.709152 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +0.679671 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +0.652405 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +0.619614 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +0.674264 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +0.645884 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +0.634227 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +0.641307 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +0.616682 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +0.58207 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +0.590639 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +0.509528 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +0.535128 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +0.576487 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +0.595619 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +0.641488 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +0.580577 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +0.53725 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +0.503354 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +0.53725 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +0.702721 0 4.94269 0 0.581753 0 0.813365 +0.641488 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +0.595619 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +0.576487 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +0.535128 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +0.509528 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +0.521021 0 4.71935 0 -0.581753 0 0.813365 +0.590639 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +0.58207 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +0.616682 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +0.641307 -0.117679 4.896 0 0 0.680563 0.73269 +0.634227 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +0.645884 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +0.674264 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +0.619614 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +0.652405 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +0.679671 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +0.709152 -0.102103 4.896 0 0 0.866398 0.499355 +0.712675 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +0.689693 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +0.724338 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +0.776817 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +0.781507 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +0.756412 -0.0509946 4.896 0 0 0.975141 0.221587 +0.712256 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +0.657685 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/resources/panoramas/usl_bay6_std_panorama.txt b/astrobee/behaviors/inspection/resources/panoramas/usl_bay6_std_panorama.txt new file mode 100644 index 00000000..982e1c8c --- /dev/null +++ b/astrobee/behaviors/inspection/resources/panoramas/usl_bay6_std_panorama.txt @@ -0,0 +1,54 @@ +-0.342315 0.00491362 4.95184 -0.386042 -0.112867 0.878762 -0.256923 +-0.287744 0.0307206 4.93535 -0.200113 -0.0466859 0.953067 -0.222348 +-0.243588 0.0509946 4.896 0 0 0.975141 -0.221587 +-0.218493 0.0648926 4.84043 0.200113 0.0466859 0.953067 -0.222348 +-0.223183 0.0810861 4.77804 0.386042 0.112867 0.878762 -0.256923 +-0.275662 0.105401 4.71935 0.516478 0.267745 0.722102 -0.374342 +-0.310307 0.145197 4.77804 -0.319707 -0.244038 -0.727761 0.555514 +-0.287325 0.132578 4.84043 -0.173596 -0.109951 -0.826776 0.523657 +-0.290848 0.102103 4.896 1.60178e-17 -1.60178e-17 0.866398 -0.499355 +-0.320329 0.0627636 4.93535 0.173596 0.109951 -0.826776 0.523657 +-0.347595 0.00879856 4.95184 0.319707 0.244038 -0.727761 0.555514 +-0.380386 -0.0430836 4.94269 -0.516478 -0.267745 0.722102 -0.374342 +-0.325736 -0.0468057 4.94269 0.302221 0.497091 -0.422544 0.694996 +-0.354116 0.00813976 4.95184 0.21067 0.342615 -0.479557 0.779909 +-0.365773 0.067608 4.93535 0.127733 0.160963 -0.608347 0.766609 +-0.358693 0.117679 4.896 -2.60437e-17 -4.97198e-17 -0.680563 0.73269 +-0.383318 0.142811 4.84043 -0.127733 -0.160963 -0.608347 0.766609 +-0.41793 0.134325 4.77804 -0.21067 -0.342615 -0.479557 0.779909 +-0.409361 0.114507 4.71935 -0.302221 -0.497091 -0.422544 0.694996 +-0.490472 0.0540852 4.77804 -0.0734955 -0.395431 -0.167301 0.900135 +-0.464872 0.0911572 4.84043 -0.0676352 -0.194037 -0.322122 0.924128 +-0.423513 0.0923031 4.896 -1.54056e-17 -9.24333e-17 -0.434171 0.90083 +-0.404381 0.0431545 4.93535 0.0676352 0.194037 -0.322122 0.924128 +-0.358512 0.00327743 4.95184 0.0734955 0.395431 -0.167301 0.900135 +-0.419423 2.62013e-17 4.93535 -1.41804e-17 0.205487 -1.13443e-16 0.97866 +-0.46275 0.0348051 4.896 -2.80695e-17 -2.80695e-17 -0.149147 0.988815 +-0.496646 1.96509e-17 4.84043 2.83608e-17 -0.205487 -9.92628e-17 0.97866 +-0.46275 -0.0348051 4.896 -1.40348e-17 -1.40348e-17 0.149147 0.988815 +-0.297279 0 4.94269 0 0.581753 0 0.813365 +-0.358512 -0.00327743 4.95184 -0.0734955 0.395431 0.167301 0.900135 +-0.404381 -0.0431545 4.93535 -0.0676352 0.194037 0.322122 0.924128 +-0.423513 -0.0923031 4.896 -3.08111e-17 -3.08111e-17 0.434171 0.90083 +-0.464872 -0.0911572 4.84043 0.0676352 -0.194037 0.322122 0.924128 +-0.490472 -0.0540852 4.77804 0.0734955 -0.395431 0.167301 0.900135 +-0.478979 0 4.71935 0 -0.581753 0 0.813365 +-0.409361 -0.114507 4.71935 0.302221 -0.497091 0.422544 0.694996 +-0.41793 -0.134325 4.77804 0.21067 -0.342615 0.479557 0.779909 +-0.383318 -0.142811 4.84043 0.127733 -0.160963 0.608347 0.766609 +-0.358693 -0.117679 4.896 0 0 0.680563 0.73269 +-0.365773 -0.067608 4.93535 -0.127733 0.160963 0.608347 0.766609 +-0.354116 -0.00813976 4.95184 -0.21067 0.342615 0.479557 0.779909 +-0.325736 0.0468057 4.94269 -0.302221 0.497091 0.422544 0.694996 +-0.380386 0.0430836 4.94269 -0.516478 0.267745 0.722102 0.374342 +-0.347595 -0.00879856 4.95184 -0.319707 0.244038 0.727761 0.555514 +-0.320329 -0.0627636 4.93535 -0.173596 0.109951 0.826776 0.523657 +-0.290848 -0.102103 4.896 0 0 0.866398 0.499355 +-0.287325 -0.132578 4.84043 0.173596 -0.109951 0.826776 0.523657 +-0.310307 -0.145197 4.77804 0.319707 -0.244038 0.727761 0.555514 +-0.275662 -0.105401 4.71935 0.516478 -0.267745 0.722102 0.374342 +-0.223183 -0.0810861 4.77804 0.386042 -0.112867 0.878762 0.256923 +-0.218493 -0.0648926 4.84043 0.200113 -0.0466859 0.953067 0.222348 +-0.243588 -0.0509946 4.896 0 0 0.975141 0.221587 +-0.287744 -0.0307206 4.93535 -0.200113 0.0466859 0.953067 0.222348 +-0.342315 -0.00491362 4.95184 -0.386042 0.112867 0.878762 0.256923 diff --git a/astrobee/behaviors/inspection/scripts/field_of_view_calculator.py b/astrobee/behaviors/inspection/scripts/field_of_view_calculator.py index fb174a6f..1ded8111 100755 --- a/astrobee/behaviors/inspection/scripts/field_of_view_calculator.py +++ b/astrobee/behaviors/inspection/scripts/field_of_view_calculator.py @@ -7,22 +7,56 @@ """ import math -from math import atan +from math import atan, tan +import scipy.optimize -def imageFov1(imageSizePixels, focalLengthPixels): + +def tsai_distort(ru, kappa): + # http://www.vision.caltech.edu/bouguetj/calib_doc/papers/Tsai.pdf eq 6 - Tsai model + ru2 = ru**2 + coeff = 1 + kappa[0] * ru2 + kappa[1] * ru2**2 + # normally it would be ru = rd * coeff, but see camera_params.cc - the sense is inverted + rd = ru * coeff + return rd + + +def tsai_undistort(rd, kappa): + # solve for ru in: rd = tsai_distort(ru, kappa) + def func(ru): + return tsai_distort(ru, kappa) - rd + + ru0 = rd # initial guess: no distortion + ru = scipy.optimize.fsolve(func, ru0) + return ru + + +def imageFov1(imageSizePixels, focalLengthPixels, omega=None, kappa=None): """ :param int imageSizePixels: Image size (pixels). :param float focalLengthPixels: Focal length (pixels). + :param float omega: Distortion coefficient omega for FOV model. + :param float kappa: Radial distortion coefficients kappa for Tsai model. :return: Image field of view (degrees). """ - return 2 * atan(imageSizePixels / (2 * focalLengthPixels)) * 180 / math.pi + rd = (imageSizePixels / 2) / focalLengthPixels + ru = rd + if omega is not None: + # https://hal.inria.fr/inria-00267247/document eq. 14 - FOV model + ru = tan(rd * omega) / (2 * tan(omega / 2)) + elif kappa is not None: + ru = tsai_undistort(rd, kappa) + return 2 * atan(ru) * 180 / math.pi -def imageFov(imageSizePixels, focalLengthPixels): +def imageFov(config): + imageSizePixels = config["imageSizePixels"] + focalLengthPixels = config["focalLengthPixels"] + omega = config.get("omega") + kappa = config.get("kappa") return ( - imageFov1(imageSizePixels[0], focalLengthPixels[0]), - imageFov1(imageSizePixels[1], focalLengthPixels[1]), + imageFov1(imageSizePixels[0], focalLengthPixels[0], omega, kappa), + imageFov1(imageSizePixels[1], focalLengthPixels[1], omega, kappa), ) @@ -36,10 +70,17 @@ def imageFov(imageSizePixels, focalLengthPixels): # File: cameras.config # Field: .{width, height} -# To find focalLengthPixels, look at: +# To find focalLengthPixels, omega, kappa, look at: # File: robots/.config -# Field: robot_camera_calibrations..intrinsic_matrix -# Entries: The first two diagonal matrix entries [0, 0] and [1, 1]. +# focalLengthPixels: +# Field: robot_camera_calibrations..intrinsic_matrix +# Entries: The first two diagonal matrix entries [0, 0] and [1, 1]. +# omega: +# Field: robot_camera_calibrations..distortion_coeff +# Entries: If the value is a scalar, interpret it as omega [FOV model]. +# kappa: +# Field: robot_camera_calibrations..distortion_coeff +# Entries: If the value is a length 4+ vector, interpret entries 0, 1, and 4 (if present) as kappa [Tsai model]. CONFIGS = [ { @@ -47,14 +88,28 @@ def imageFov(imageSizePixels, focalLengthPixels): # Note: SciCam was calibrated using 1/4 resolution images. Full res is 4x bigger. "imageSizePixels": [1336, 1002], "focalLengthPixels": [1138.4943, 1138.4943], + "kappa": [-0.025598438, 0.0056673533], }, { "name": "HazCam", "imageSizePixels": [224, 171], "focalLengthPixels": [215.88697, 215.88697], + "kappa": [-0.259498, -0.00024045673], + }, + { + "name": "NavCam", + "imageSizePixels": [1280, 960], + "focalLengthPixels": [608.8073, 607.61439], + "omega": 0.998693, + }, + { + "name": "DockCam", + "imageSizePixels": [1280, 960], + "focalLengthPixels": [753.51021, 751.3611], + "omega": 1.00762, }, ] for config in CONFIGS: - fov = imageFov(config["imageSizePixels"], config["focalLengthPixels"]) + fov = imageFov(config) print("%s FOV: %.1f x %.1f degrees" % (config["name"], fov[0], fov[1])) diff --git a/astrobee/behaviors/inspection/scripts/pano_orientations.py b/astrobee/behaviors/inspection/scripts/pano_orientations.py index 36a57cb1..11214f4b 100755 --- a/astrobee/behaviors/inspection/scripts/pano_orientations.py +++ b/astrobee/behaviors/inspection/scripts/pano_orientations.py @@ -1,86 +1,126 @@ #!/usr/bin/env python +# +# Copyright (c) 2021, 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. """ This is a reference implementation for how to generate panorama orientations given a configuration that includes the desired pan and tilt ranges, camera field of view, -desired overlap between consecutive images, and a pre-tilt parameter that helps -with efficiently capturing directional panoramas near tilt = +/- 90. +desired overlap between consecutive images, and attitude tolerance to account +for the possibility of pointing error. The main function of interest is panoOrientations(). There is also some -validation logic, and two of the test cases are candidates for the configurations to -test during the SoundSee-Data-3 ISS activity. +validation logic, and some of the test cases are candidates for the configurations to +test during ISAAC ISS activities. """ +from __future__ import print_function + +import argparse +import csv +import logging import math import numpy as np -from scipy.spatial.transform import Rotation EPS = 1e-5 -ROLL, PITCH, YAW = 0, 1, 2 - -def fromRPY(roll, pitch, yaw): - return Rotation.from_euler("XYZ", (roll, pitch, yaw), degrees=True) - -def toRPY(rot): - return rot.as_euler("XYZ", degrees=True) +def get_h_fov_effective(h_fov, v_fov, tilt): + """ + Returns the effective HFOV for an image with tilt centered at @tilt. + At large tilt values ("high latitude"), effective HFOV is larger + because the parallels are shorter near the pole. We conservatively + take the effective HFOV from either the middle, top, or bottom edge + of the image, whichever is smallest. + + :param float h_fov: Horizontal field of view of each image (degrees). + :param float v_fov: Vertical field of view of each image (degrees). + :param float tilt: The tilt of the image center (degrees). + :return: The effective HFOV (degrees). + """ + v_radius = 0.5 * v_fov + min_abs_theta = min(abs(tilt - v_radius), abs(tilt), abs(tilt + v_radius)) + return h_fov / math.cos(min_abs_theta * math.pi / 180) -def pano1D(rangeMin, rangeMax, fov, overlap): +def pano_1d(range_radius, fov, overlap, attitude_tolerance): """ Returns image center coordinates such that images cover the range - @rangeMin .. @rangeMax with at least the specified @overlap. If one - image suffices, it will be centered between @rangeMin and @rangeMax (and - the edges of the image will beyond the range if it is smaller than @fov). - If more than one image is needed to cover the range, the boundary images - will cover exactly to the edges of the specified range and the images - will be evenly spaced. - - :param float rangeMin: Minimum angle of minimum image (degrees). - :param float rangeMax: Maximum angle of maximum image (degrees). + -@range_radius .. +@range_radius with at least the specified + @overlap. If one image suffices, it will be centered at 0 (and the + edges of the image will extend beyond the range if it is smaller + than @fov). If more than one image is needed to cover the range, + the boundary images will cover exactly to the edges of the specified + range (modulo @attitude_tolerance) and the images will be evenly spaced. + + :param float range_radius: Images must cover -range_radius .. +range_radius (degrees). :param float fov: Field of view of each image (degrees). :param float overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). + :param float attitude_tolerance: Ensure overlap criterion is met even if relative attitude between a pair of adjacent images, or between an image and the desired pano boundary, is off by at most this much (degrees). :return: A vector of orientations of image centers. """ - assertLte(rangeMin, rangeMax, EPS) - W = rangeMax - rangeMin + W = range_radius * 2 - if W < fov: + if (W + 2 * attitude_tolerance - fov) < 0: # Special case: Only one image needed. Center it. - return np.array([0.5 * (rangeMin + rangeMax)]) - - # sufficient overlap criterion: stride <= fov * (1 - overlap) - # (k - 1) * stride + fov = W - # stride = (W - fov) / (k - 1) - # (W - fov) / (k - 1) <= fov * (1 - overlap) - # k - 1 >= (W - fov) / (fov * (1 - overlap)) - # k >= (W - fov) / (fov * (1 - overlap)) + 1 - - k = math.ceil((W - fov) / (fov * (1 - overlap))) + 1 + return np.array([0]) + + # sufficient overlap criterion: stride <= fov * (1 - overlap) - attitude_tolerance + # (k - 1) * stride + fov = W + 2 * attitude_tolerance + # stride = (W + 2 * attitude_tolerance - fov) / (k - 1) + # (W + 2 * attitude_tolerance - fov) / (k - 1) <= fov * (1 - overlap) - attitude_tolerance + # k - 1 >= (W + 2 * attitude_tolerance - fov) / (fov * (1 - overlap) - attitude_tolerance) + # k >= (W + 2 * attitude_tolerance - fov) / (fov * (1 - overlap) - attitude_tolerance) + 1 + + k = ( + math.ceil( + (W + 2 * attitude_tolerance - fov) + / (fov * (1 - overlap) - attitude_tolerance) + ) + + 1 + ) if 1: # optional sanity checks - stride = (W - fov) / (k - 1) - assertLte(stride, fov * (1 - overlap), EPS) # sufficient overlap + stride = (W + 2 * attitude_tolerance - fov) / (k - 1) + assert_lte( + stride, fov * (1 - overlap) - attitude_tolerance, EPS + ) # sufficient overlap # check if we have more images than necessary if k == 1: pass # obviously need at least one image elif k == 2: - assertLte(fov, W, EPS) # k = 1 is not enough + assert_lte(fov, W + 2 * attitude_tolerance, EPS) # k = 1 is not enough else: - stride1 = (W - fov) / (k - 2) - assertGte(stride1, fov * (1 - overlap), EPS) # k is minimized + stride1 = (W + 2 * attitude_tolerance - fov) / (k - 2) + assert_gte( + stride1, fov * (1 - overlap) - attitude_tolerance, EPS + ) # k is minimized - minCenter = rangeMin + fov / 2 - maxCenter = rangeMax - fov / 2 - return np.linspace(minCenter, maxCenter, num=k) + min_center = -(range_radius + attitude_tolerance) + fov / 2 + max_center = -min_center + return np.linspace(min_center, max_center, num=k) -def pano1DCompletePan(fov, overlap): +def pano_1d_complete_pan(fov, overlap, attitude_tolerance): """ Returns image center coordinates such that the images cover the full pan range -180 .. 180 with at least the specified @overlap between all consecutive images, @@ -88,65 +128,134 @@ def pano1DCompletePan(fov, overlap): :param float fov: Field of view of each image (degrees). :param float overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). - :return: A vector of orientations of image centers. + :param float attitude_tolerance: Ensure overlap criterion is met even if relative attitude between a pair of adjacent images, or between an image and the desired pano boundary, is off by at most this much (degrees). + :return: A vector of orientations of image centers (degrees). """ - k = math.ceil(360 / (fov * (1 - overlap))) + k = math.ceil(360 / (fov * (1 - overlap) - attitude_tolerance)) centers = np.linspace(-180, 180, num=k, endpoint=False) # ensure pano is centered at pan = 0 - midPoint = np.mean((centers[0], centers[-1])) - return centers - midPoint + mid_point = np.mean((centers[0], centers[-1])) + return centers - mid_point + + +def pano_1d_pan(pan_radius, h_fov, v_fov, overlap, attitude_tolerance, tilt): + """ + Returns image center coordinates for a row of images at tilt value + @tilt that cover the range -@pan_radius .. +@pan_radius with at least + the specified @overlap. Special complete wrap-around behavior is + triggered when @pan_radius is exactly 180. + + :param float pan_radius: Cover pan angle of -pan_radius to +pan_radius (degrees). + :param float h_fov: Horizontal field of view of each image (degrees). + :param float v_fov: Vertical field of view of each image (degrees). + :param float overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). + :param float attitude_tolerance: Ensure overlap criterion is met even if relative attitude between a pair of adjacent images is off by at most this much (degrees). + :param float tilt: The tilt of the image center (degrees). + :return: A vector of pan orientations of image centers (degrees). + """ + h_fov_effective = get_h_fov_effective(h_fov, v_fov, tilt) + # print("tilt=%s h_fov_effective=%s" % (tilt, h_fov_effective)) + if pan_radius == 180: + return pano_1d_complete_pan(h_fov_effective, overlap, attitude_tolerance) + else: + return pano_1d(pan_radius, h_fov_effective, overlap, attitude_tolerance) -def panoOrientations(panMin, panMax, tiltMin, tiltMax, hFov, vFov, overlap, preTilt): +def pano_orientations( + pan_radius, tilt_radius, h_fov, v_fov, overlap, attitude_tolerance +): """ Return image center coordinates that cover the specified pan and tilt ranges, with the specified @overlap. Special complete wrap-around behavior is triggered when - the pan range is exactly -180 .. 180. The @preTilt parameter can be used to re-center - the panorama, enabling efficient directional panoramas near tilt = +/- 90. - - :param float panMin: Pan angle of left edge of leftmost column (degrees). - :param float panMax: Pan angle of right edge of rightmost column (degrees). - :param float tiltMin: Tilt angle of bottom edge of bottom row (degrees). - :param float tiltMax: Tilt angle of top edge of top row (degrees). - :param float hFov: Horizontal field of view of each image (degrees). - :param float vFov: Vertical field of view of each image (degrees). + the pan_radius is exactly 180. + + :param float pan_radius: Cover pan angle of -pan_radius to +pan_radius (degrees). + :param float tilt_radius: Cover tilt angle of -tilt_radius to +tilt_radius (degrees). + :param float h_fov: Horizontal field of view of each image (degrees). + :param float v_fov: Vertical field of view of each image (degrees). :param float overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). - :param float preTilt: Offsets the (pan, tilt) = (0, 0) center in tilt (degrees) so as to efficiently capture panoramas centered near tilt = +/- 90. Example: When preTilt = -90, tilt = 0 is offset to point straight down. - :return: (imageCenters, ncols, nrows). A list of orientations of image centers, the number of columns in the panorama, and the number of rows. + :param float attitude_tolerance: Ensure overlap criterion is met even if relative attitude between a pair of adjacent images is off by at most this much (degrees). + :return: (image_centers, nrows, ncols). A list of orientations of image centers, the number of rows in the panorama, and the number of columns. """ - if panMin == -180 and panMax == 180: - panVals = pano1DCompletePan(hFov, overlap) - else: - panVals = pano1D(panMin, panMax, hFov, overlap) - tiltVals = pano1D(tiltMin, tiltMax, vFov, overlap) - - # Images are in column-major order. When capturing a long panorama with - # people present, this makes it easier for them to move out of the field of - # view as needed. Order columns left to right, and within each column, tilt - # top to bottom. That is just more intuitive for people's expectations. - imageCenters = [] - preRot = fromRPY(0, preTilt, 0) - for pan in panVals: - for tilt in reversed(tiltVals): - rot = fromRPY(0, tilt, pan) - imageCenters.append(rot * preRot) - return (imageCenters, len(panVals), len(tiltVals)) - - -def printPano(pano): - imageCenters, ncols, nrows = pano - print("%s cols x %s rows [roll pitch yaw]" % (ncols, nrows)) - for i in range(nrows): + + # calculate all image centers + image_centers = [] + tilt_vals = pano_1d(tilt_radius, v_fov, overlap, attitude_tolerance) + for iy, tilt in enumerate(reversed(tilt_vals)): + pan_vals = pano_1d_pan( + pan_radius, h_fov, v_fov, overlap, attitude_tolerance, tilt + ) + for pan in pan_vals: + image_centers.append((pan, tilt, iy, -1)) + # image_centers = np.array(image_centers) + # print(image_centers.shape) + image_centers = np.array( + image_centers, + dtype=[ + ("pan", np.double), + ("tilt", np.double), + ("iy", np.int16), + ("ix", np.int16), + ], + ) + + # assign image centers to columns + min_tilt = np.min(np.abs(tilt_vals)) + column_centers = pano_1d_pan( + pan_radius, h_fov, v_fov, overlap, attitude_tolerance, min_tilt + ) + image_pans = image_centers["pan"] + image_centers["ix"] = np.array( + [np.argmin(np.abs(column_centers - p)) for p in image_pans] + ) + + # order images in column-major order; alternate direction top-to-bottom or bottom-to-top + images_ordered = [] + for ix, pan in enumerate(column_centers): + images_in_col_ind = image_centers["ix"] == ix + images_in_col = image_centers[images_in_col_ind] + + # sort on tilt bottom-to-top + images_in_col = images_in_col[np.argsort(images_in_col["tilt"])] + + # on even-numbered columns, reverse tilt order, top-to-bottom + if ix % 2 == 0: + images_in_col = images_in_col[::-1] + + images_ordered += list(images_in_col) + images_ordered = np.array(images_ordered) + + nrows = len(tilt_vals) + ncols = len(column_centers) + + return images_ordered, nrows, ncols + + +def print_pano(pano): + image_centers, nrows, ncols = pano + num_images = image_centers.shape[0] + print( + "%s images, %s rows x %s cols, frame# [pan tilt]:" % (num_images, nrows, ncols) + ) + if 0: + print(image_centers) + return + image_lookup = { + (iy, ix): (i, pan, tilt) for i, (pan, tilt, iy, ix) in enumerate(image_centers) + } + for iy in range(nrows): print(" ", end="") - for j in range(ncols): - # print(i, j, j * nrows + i) - imageCenter = imageCenters[j * nrows + i] - # print(np.around(getEuler(imageCenter, preTilt=-30)), end=" ") - print(np.around(toRPY(imageCenter)), end=" ") + for ix in range(ncols): + image_info = image_lookup.get((iy, ix)) + if image_info is None: + print(" ", end=" ") + else: + i, pan, tilt = image_info + print("%2d [%4d %4d]" % (i, round(pan), round(tilt)), end=" ") print() -def assertEqual(a, b, eps): +def assert_equal(a, b, eps): assert abs(a - b) < eps, "FAIL: %s should equal %s, within tolerance %s" % ( a, b, @@ -154,233 +263,48 @@ def assertEqual(a, b, eps): ) -def assertLte(a, b, eps): +def assert_lte(a, b, eps): assert a <= b + eps, "FAIL: %s should be <= %s, within tolerance %s" % (a, b, eps) -def assertGte(a, b, eps): - assert a >= b + eps, "FAIL: %s should be >= %s, within tolerance %s" % (a, b, eps) - - -def getEuler(rot, preTilt): - invPreRot = fromRPY(0, -preTilt, 0) - return toRPY(rot * invPreRot) +def assert_gte(a, b, eps): + assert a >= b - eps, "FAIL: %s should be >= %s, within tolerance %s" % (a, b, eps) -def checkPano(pano, panMin, panMax, tiltMin, tiltMax, hFov, vFov, overlap, preTilt): - imageCenters, ncols, nrows = pano - - topLeft = getEuler(imageCenters[0], preTilt) - - if ncols == 1: - assertLte(panMax - panMin, hFov, EPS) # one column is enough - else: - nextPan = getEuler(imageCenters[nrows], preTilt) - panStride = nextPan[YAW] - topLeft[YAW] - assertLte(panStride, hFov * (1 - overlap), EPS) # pan overlaps enough - - if nrows == 1: - assertLte(tiltMax - tiltMin, vFov, EPS) # one row is enough - else: - nextTilt = getEuler(imageCenters[1], preTilt) - tiltStride = -(nextTilt[PITCH] - topLeft[PITCH]) - assertLte(tiltStride, vFov * (1 - overlap), EPS) # tilt overlaps enough - - # we shouldn't be able to remove a column - if ncols == 1: - pass # obviously can't remove single column - elif ncols == 2: - if panMin == -180 and panMax == 180: - assertLte(hFov * (1 - overlap), 360) # one column is not enough - else: - assertLte(hFov, panMax - panMin, EPS) # one column is not enough - else: - if panMin == -180 and panMax == 180: - panStride1 = 360 / (ncols - 1) - else: - panStride1 = ((panMax - panMin) - hFov) / (ncols - 2) - assertGte(panStride1, hFov * (1 - overlap), EPS) # ncols is minimized - - # we shouldn't be able to remove a row - if nrows == 1: - pass # obviously can't remove single row - elif nrows == 2: - assertLte(vFov, tiltMax - tiltMin, EPS) # one row is not enough - else: - tiltStride1 = ((tiltMax - tiltMin) - vFov) / (nrows - 2) - assertGte(tiltStride1, vFov * (1 - overlap), EPS) # nrows is minimized - - bottomRight = getEuler(imageCenters[-1], preTilt) +def test_case(config): + label = config["label"] + print(label, end=": ") + pano = pano_orientations( + float(config["pan_radius_degrees"]), + float(config["tilt_radius_degrees"]), + float(config["h_fov_degrees"]), + float(config["v_fov_degrees"]), + float(config["overlap"]), + float(config["attitude_tolerance_degrees"]), + ) + print_pano(pano) + print() - panCenterMin = topLeft[YAW] - panCenterMax = bottomRight[YAW] - if panMin == -180 and panMax == 180: - assertEqual(panStride * ncols, 360, EPS) # evenly spaced - midPan = np.mean((panCenterMin, panCenterMax)) - assertEqual(midPan, 0, EPS) # centered - else: - if ncols == 1: - assertEqual( - panCenterMin, 0.5 * (panMin + panMax), EPS - ) # single column is centered - else: - assertEqual(panCenterMin - hFov / 2, panMin, EPS) # covers to panMin - assertEqual(panCenterMax + hFov / 2, panMax, EPS) # covers to panMax - tiltCenterMax = topLeft[PITCH] - tiltCenterMin = bottomRight[PITCH] +def do_cases(csv_path): + with open(csv_path, "r") as csv_stream: + rows = csv.DictReader(csv_stream) + for row in rows: + test_case(row) - if nrows == 1: - assertEqual( - tiltCenterMin, 0.5 * (tiltMin + tiltMax), EPS - ) # single row is centered - else: - assertEqual(tiltCenterMin - vFov / 2, tiltMin, EPS) # covers to tiltMin - assertEqual(tiltCenterMax + vFov / 2, tiltMax, EPS) # covers to tiltMax +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "in_csv", + nargs="?", + help="input file of test cases", + default="pano_test_cases.csv", + ) -def testCase(label, config): - print(label, end=": ") - pano = panoOrientations(**config) - printPano(pano) - checkPano(pano, **config) - - -# Based on field_of_view_calculator.py, consulting with Oleg, we have: -SCI_CAM_FOV = [60.8, 47.5] # degrees -HAZ_CAM_FOV = [54.8, 43.2] # degrees - -# For panorama configuration, to ensure all sensors have enough overlap, we -# need to use the combination of (sensor FOV, overlap) that translates to the -# tightest image spacing. - -# The justification for requiring overlap is partly in order to use features in -# overlapping areas to guide registration, and partly in order to tolerate -# attitude control errors while collecting the panorama (with more overlap, we -# are less likely to have accidental gaps in coverage due to pointing errors). - -# Overlapping for registration is not really relevant for the HazCam, given -# that the geometry mapper currently exclusively relies on NavCam for -# registration, but it is relevant for registering SciCam images using -# stitching tools like Hugin. Currently, we don't really know what level of -# pointing error to expect. - -# In the test cases below, we are conservatively using the smaller HazCam FOV -# with the 30% overlap required by the SciCam. Note that if we were to switch -# to basing it on the larger SciCam FOV, it would only reduce the image count -# for a full-sphere pano by ~10-15%, so this seems like an acceptable level -# of overhead in order to be appropriately conservative. - -# == TEST CASE 1 == -# Full spherical panorama. Note a small amount of padding has been added to the -# tilt range to ensure the poles are actually captured. It doesn't change the -# number of rows required. - -# These might be the right parameters for a full-coverage SciCam test pano -# during SoundSee-Data-3. -testCase( - "Full spherical panorama", - { - "panMin": -180, - "panMax": 180, - "tiltMin": -95, - "tiltMax": 95, - "hFov": HAZ_CAM_FOV[0], - "vFov": HAZ_CAM_FOV[1], - "overlap": 0.3, - "preTilt": 0, - }, -) - -# == TEST CASE 2 == -testCase( - "Directional panorama", - { - "panMin": -60, - "panMax": 60, - "tiltMin": -30, - "tiltMax": 30, - "hFov": HAZ_CAM_FOV[0], - "vFov": HAZ_CAM_FOV[1], - "overlap": 0.3, - "preTilt": 0, - }, -) - -# == TEST CASE 3 == -# This test case with a small amount of pre-tilt is useful for sanity checking. -# We expect preTilt to have a straightforward first-order effect on the pitch -# values but also a second-order effect on the roll and yaw. -testCase( - "Directional panorama with small pre-tilt", - { - "panMin": -60, - "panMax": 60, - "tiltMin": -30, - "tiltMax": 30, - "hFov": HAZ_CAM_FOV[0], - "vFov": HAZ_CAM_FOV[1], - "overlap": 0.3, - "preTilt": -10, - }, -) - -# == TEST CASE 4 == -# The results look much stranger with pre-tilt set to -90. For example, in the -# center column of the pano, we expect both the roll and yaw values to flip -# from 0 (upper part of pano) to +/- 180 (lower part of pano) as the pano -# crosses the pole. Note: If you see a warning about gimbal lock right at the -# pole, it should be harmless in this context. It just indicates that there -# are multiple valid RPY values that would give the same desired rotation, so -# it is arbitrarily picking one. - -# These might be the right parameters for a SoundSee test pano during -# SoundSee-Data-3. (The FOV parameters are a bit arbitrary for this type of -# "virtual imaging" sensor, can double-check with SoundSee folks.) -testCase( - "Directional panorama centered straight down", - { - "panMin": -60, - "panMax": 60, - "tiltMin": -60, - "tiltMax": 60, - "hFov": 60, - "vFov": 60, - "overlap": 0.3, - "preTilt": -90, - }, -) - -# == TEST CASE 5 == -# Test special-case logic for 1-row panorama. - -testCase( - "1-row panorama", - { - "panMin": -60, - "panMax": 60, - "tiltMin": 10, - "tiltMax": 20, - "hFov": 60, - "vFov": 60, - "overlap": 0.3, - "preTilt": 0, - }, -) - -# == TEST CASE 6 == -# Test special-case logic for 1-column panorama. - -testCase( - "1-column panorama", - { - "panMin": 0, - "panMax": 0, - "tiltMin": -60, - "tiltMax": 60, - "hFov": 60, - "vFov": 60, - "overlap": 0.3, - "preTilt": 0, - }, -) + args = parser.parse_args() + logging.basicConfig(level=logging.INFO, format="%(message)s") + do_cases(args.in_csv) diff --git a/astrobee/behaviors/inspection/scripts/panorama_maker.sh b/astrobee/behaviors/inspection/scripts/panorama_maker.sh deleted file mode 100755 index b0aa2a68..00000000 --- a/astrobee/behaviors/inspection/scripts/panorama_maker.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# - -# generate pto file with images -pto_gen -o panorama.pto -f 62.0 *.jpg - -# generate control points -cpfind --multirow -o panorama.pto panorama.pto - -# set optimization variables, optimize -pto_var --opt y,p,r,b -o panorama.pto panorama.pto -autooptimiser -n -o panorama.pto panorama.pto - -# clean outliers -cpclean --max-distance=3 -o panorama.pto panorama.pto - -# optimize pohotometric parameters -pto_var --opt y,p,r,TrX,TrY,TrZ,b -o panorama.pto panorama.pto -autooptimiser -n -o panorama.pto panorama.pto - - -autooptimiser -m -o panorama.pto panorama.pto - -# configure image output -pano_modify -o panorama.pto --center --canvas=AUTO --projection=2 --fov=360x180 panorama.pto - -# generate panorama -hugin_executor --stitching --prefix=prefix panorama.pto \ No newline at end of file diff --git a/astrobee/behaviors/inspection/scripts/plot_pano.py b/astrobee/behaviors/inspection/scripts/plot_pano.py new file mode 100755 index 00000000..13fe2395 --- /dev/null +++ b/astrobee/behaviors/inspection/scripts/plot_pano.py @@ -0,0 +1,552 @@ +#!/usr/bin/env python +# +# Copyright (c) 2021, 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. + +""" +Debugs pano coverage. + +Reads pano_test_cases.csv to extract test case parameters and the +case_*.csv panorama coverage plans output by the test_pano tool. + +For each test case: + +- Checks if the panorama satisfies the coverage requirement and the + image overlap requirement (tolerating pointing error up to attitude + tolerance). +- Outputs several plots to help understand coverage. + +Note that, unfortunately, the pano_orientations() function does *not* guarantee +its output satisfies the coverage and overlap requirements. This is because +spherical trig warps the image area to be a non-rectangle in a way that's +difficult to correct for in general. You can use this tool to check if there is +a problem with an output plan. If there is, you can encourage the coverage +planner to add more images by adding some extra "warp margin" padding to +plan_attitude_tolerance_degrees while leaving test_attitude_tolerance_degrees +unchanged, until the problem is resolved. +""" + +from __future__ import print_function + +import argparse +import itertools +import logging + +import matplotlib +import numpy as np +import pandas as pd +from scipy.spatial.transform import Rotation + +if __name__ == "__main__": + matplotlib.use("Agg") # must come before importing pyplot + +from matplotlib import collections as mc +from matplotlib import colors as mcolors +from matplotlib import pyplot as plt + +EPS = 1e-5 +TEST_GRID_SPACING = 5 +FRAME_TEST_GRID_COUNT = 20 + + +def from_pan_tilt(pan, tilt): + return Rotation.from_euler("XYZ", (0, tilt, pan), degrees=True) + + +def to_pan_tilt(rot): + rpy = rot.as_euler("XYZ", degrees=True) + return rpy[2], rpy[1] + + +def add_pan_tilt1(pt, dpt): + rot = from_pan_tilt(*pt) + drot = from_pan_tilt(*dpt) + return np.array(to_pan_tilt(drot * rot)) + + +add_pan_tilt = np.vectorize(add_pan_tilt1, signature="(2),(2)->(2)") + + +def annotate_test_cases(test_cases): + num_images = [] + nrows = [] + ncols = [] + for label in test_cases["label"]: + pano = pd.read_csv("case_%s.csv" % label) + num_images.append(len(pano)) + nrows.append(pano["iy"].max() + 1) + ncols.append(pano["ix"].max() + 1) + test_cases["num_images"] = num_images + test_cases["nrows"] = nrows + test_cases["ncols"] = ncols + + +def get_test_case(in_csv_path, label): + test_cases = pd.read_csv(in_csv_path) + params = next(test_cases[test_cases["label"] == label].itertuples()) + pano = pd.read_csv("case_%s.csv" % label) + if params.pan_radius_degrees == 180: + pan_radius_adjusted = 180 + else: + pan_radius_adjusted = ( + params.pan_radius_degrees + 0.5 * params.test_attitude_tolerance_degrees + ) + return { + "label": label, + "params": params, + "pano": pano, + "h_fov_adjusted": params.h_fov_degrees - params.test_attitude_tolerance_degrees, + "v_fov_adjusted": params.v_fov_degrees - params.test_attitude_tolerance_degrees, + "pan_radius_adjusted": pan_radius_adjusted, + "tilt_radius_adjusted": params.tilt_radius_degrees + + 0.5 * params.test_attitude_tolerance_degrees, + } + + +def pairwise(seq): + """ + Like itertools.pairwise(), which is not available in some Python versions. + """ + first = True + for val in seq: + if first: + first = False + else: + yield prev, val + prev = val + + +def plot_box(ws, en, style="k-"): + w, s = ws + e, n = en + corners = [ + (w, s), + (w, n), + (e, n), + (e, s), + ] + for p0, p1 in pairwise(corners + [corners[0]]): + x0, y0 = p0 + x1, y1 = p1 + plt.plot([x0, x1], [y0, y1], style) + + +def plot_style_lon_lat(fig, ax): + """ + If your plot (x, y) can be interpreted as (lon, lat) covering the whole sphere, + use this function to set up nice plot bounds and grid ticks. + """ + ax.axis("equal") + plt.xticks(np.arange(-180, 181, 10)) + plt.yticks(np.arange(-90, 91, 10)) + ax.set_xlim((-186, 186)) + ax.set_ylim((-93, 93)) + plot_box((-180, -90), (180, 90)) + plt.grid() + plt.xlabel("Pan (degrees)") + plt.ylabel("Tilt (degrees)") + fig.set_size_inches((20, 10)) + + +def plot_required_coverage(test_case): + pan_radius = test_case["pan_radius_adjusted"] + tilt_radius = test_case["tilt_radius_adjusted"] + plot_box((-pan_radius, -tilt_radius), (pan_radius, tilt_radius), style="g--") + + +def plot_pano_seq(test_case, out_path=None): + """ + Plot sequence of frame centers. + """ + fig, ax = plt.subplots() + pano = test_case["pano"] + plt.plot(pano["pan"], pano["tilt"], "o-") + plt.plot(pano["pan"][0], pano["tilt"][0], "ro") # first frame red + for frame in pano.itertuples(): + ax.annotate(frame.Index, (frame.pan + 3, frame.tilt + 3)) + plot_required_coverage(test_case) + plot_style_lon_lat(fig, ax) + plt.title("Sequence of image center pan/tilt values") + if out_path: + plt.savefig(out_path) + logging.info("plot_pano_seq: wrote to %s", out_path) + plt.close() + + +def linspace_pts(pt0, pt1, k=20): + x = np.linspace(0, 1, k)[:, np.newaxis] + return (1 - x) * pt0[np.newaxis, :] + x * pt1[np.newaxis, :] + + +def get_image_borders_dpt_internal(h_fov, v_fov): + h_rad = 0.5 * h_fov + v_rad = 0.5 * v_fov + corners = [ + np.array([-h_rad, -v_rad]), + np.array([-h_rad, v_rad]), + np.array([h_rad, v_rad]), + np.array([h_rad, -v_rad]), + ] + result = np.zeros((0, 2)) + for pt0, pt1 in pairwise(corners + [corners[0]]): + result = np.vstack((result, linspace_pts(pt0, pt1))) + return result + + +def get_image_borders_dpt(test_case): + return get_image_borders_dpt_internal( + test_case["h_fov_adjusted"], test_case["v_fov_adjusted"] + ) + + +def plot_frame_borders(ax, fborders, color): + fborder_pairs = np.column_stack([fborders[:-1, :], fborders[1:, :]]) + long_segments = np.abs(fborder_pairs[:, 0] - fborder_pairs[:, 2]) > 270 + fborder_pairs = fborder_pairs[~long_segments] + fbp_list = [((x0, y0), (x1, y1)) for x0, y0, x1, y1 in fborder_pairs] + mcolor = mcolors.to_rgba(color) + lc = mc.LineCollection(fbp_list, colors=[mcolor] * len(fbp_list)) + ax.add_collection(lc) + + +def plot_pano_borders(test_case, out_path=None): + fig, ax = plt.subplots() + borders = get_image_borders_dpt(test_case) + colors = itertools.cycle( + ("r", "g", "b", "m", "k", "c", "#444400", "#440044", "#004444") + ) + pano = test_case["pano"] + for frame in pano.itertuples(): + pt = np.array([frame.pan, frame.tilt]) + fborders = add_pan_tilt(pt, borders) + plot_frame_borders(ax, fborders, next(colors)) + plot_style_lon_lat(fig, ax) + plot_required_coverage(test_case) + plt.title("Pano image borders (FOV reduced to represent attitude tolerance)") + if out_path: + plt.savefig(out_path) + logging.info("plot_pano_borders: wrote to %s", out_path) + plt.close() + + +def get_bounds_checker(image_pt, h_fov, v_fov): + h_radius = 0.5 * h_fov + v_radius = 0.5 * v_fov + image_rot = from_pan_tilt(*image_pt) + + def in_bounds_pt(pt): + pt_rot = from_pan_tilt(*pt) + drot = pt_rot * image_rot.inv() + dpan, dtilt = to_pan_tilt(drot) + result_bool = abs(dtilt) < v_radius + EPS + if result_bool: + if abs(90 - abs(dtilt)) < 0.1: + pass # pan check meaningless at pole + else: + result_bool &= abs(dpan) < h_radius + EPS + return 1 if result_bool else 0 + + return np.vectorize(in_bounds_pt, signature="(2)->()") + + +def get_global_test_grid(test_case): + pan_radius = test_case["pan_radius_adjusted"] + tilt_radius = test_case["tilt_radius_adjusted"] + + k_pan = int(pan_radius * 2 / TEST_GRID_SPACING) + 1 + pan = np.linspace(-pan_radius, pan_radius, k_pan) + + # check northern hemisphere only. southern is symmetrical. + k_tilt = int(tilt_radius / TEST_GRID_SPACING) + 1 + tilt = np.linspace(0, tilt_radius, k_tilt) + + tt, pp = np.meshgrid(tilt, pan) + return np.dstack((pp, tt)) + + +def get_coverage_count(test_case, test_grid): + coverage_count = np.zeros(test_grid.shape[:-1]) + pano = test_case["pano"] + for frame in pano.itertuples(): + fpt = (frame.pan, frame.tilt) + in_frame_bounds = get_bounds_checker( + fpt, test_case["h_fov_adjusted"], test_case["v_fov_adjusted"] + ) + coverage_count = coverage_count + in_frame_bounds(test_grid) + return coverage_count + + +def plot_hist_integer(arr, out_path=None): + plt.hist(arr.flatten(), bins=np.arange(arr.min() - 1, arr.max() + 1) + 0.5) + plt.xticks(np.arange(-1, arr.max() + 1)) + plt.title("Coverage count frequency histogram") + plt.xlabel("Number of pano images that include test point") + plt.ylabel("Number of test points") + if out_path: + plt.savefig(out_path) + logging.info("plot_hist_integer: wrote to %s", out_path) + plt.close() + + +def plot_coverage_count(test_case, test_grid, coverage_count, cblabel, out_path=None): + fig, ax = plt.subplots() + plt.scatter(test_grid[:, :, 0], test_grid[:, :, 1], c=coverage_count) + plot_style_lon_lat(fig, ax) + plot_required_coverage(test_case) + plt.colorbar(label=cblabel) + plt.title("Coverage count map") + if out_path: + plt.savefig(out_path) + logging.info("plot_coverage_count: wrote to %s", out_path) + plt.close() + + +def check_complete_coverage(coverage_count): + num_uncovered = np.count_nonzero(coverage_count == 0) + success = num_uncovered == 0 + print("check_complete_coverage:", "PASS" if success else "FAIL") + return success + + +def pan_distance(d): + abs_d = np.abs(d) + return np.minimum(abs_d, 360 - abs_d) + + +def get_manhattan_neighbors_internal(frame, params, frameLookup, nrows, ncols): + iy = frame.iy + ix = frame.ix + neighbors = [] + + north_south = ( + ("north", (0 <= (iy - 1)), -1), + ("south", ((iy + 1) < nrows), 1), + ) + for dir_label, bound_check, dy in north_south: + if not bound_check: + continue + neighbor_y = iy + dy + neighbor_row = [f for f in frameLookup.values() if f.iy == neighbor_y] + neighbor_row_pan = np.array([f.pan for f in neighbor_row]) + sort_ind = np.argsort(pan_distance(neighbor_row_pan - frame.pan)) + neighbor1 = neighbor_row[sort_ind[0]] + dir_neighbors = [neighbor1] + if pan_distance(neighbor1.pan - frame.pan) > 0.1 and len(neighbor_row) > 1: + # columns not aligned, need the other bracketing neigbor + dir_neighbors.append(neighbor_row[sort_ind[1]]) + neighbors.append((dir_label, dir_neighbors)) + + west_east = ( + ("west", -1), + ("east", 1), + ) + for dir_label, dx in west_east: + x_vals = ix + dx * np.arange(ncols - 1) + if params.pan_radius_degrees == 180: + # implement 360 wrap-around + x_vals = np.mod(x_vals, ncols) + else: + # stop at west/east edges + x_vals = x_vals[(0 <= x_vals) & (x_vals < ncols)] + for x in x_vals: + if x == ix: + continue + neighbor = frameLookup.get((iy, x)) + if neighbor is not None: + neighbors.append((dir_label, [neighbor])) + break + return neighbors + + +def get_manhattan_neighbors(frame, test_case): + params = test_case["params"] + pano = test_case["pano"] + frameLookup = {(frame.iy, frame.ix): frame for frame in pano.itertuples()} + # print(frameLookup) + nrows = pano["iy"].max() + 1 + ncols = pano["ix"].max() + 1 + return get_manhattan_neighbors_internal(frame, params, frameLookup, nrows, ncols) + + +def get_frame_test_grid(pt, h_fov, v_fov, k=FRAME_TEST_GRID_COUNT): + pan = np.linspace(-0.5 * h_fov, 0.5 * h_fov, k) + tilt = np.linspace(-0.5 * v_fov, 0.5 * v_fov, k) + tt, pp = np.meshgrid(tilt, pan) + test_grid_dpt = np.dstack((pp, tt)) + return add_pan_tilt(pt, test_grid_dpt) + + +def get_in_frame(test_grid, frame, h_fov, v_fov): + bounds_checker = get_bounds_checker((frame.pan, frame.tilt), h_fov, v_fov) + return bounds_checker(test_grid) + + +def frame_text(frame): + return "%s [%s %s]" % (frame.Index, int(round(frame.pan)), int(round(frame.tilt))) + + +def check_overlap(test_case, verbose=False): + params = test_case["params"] + h_fov = test_case["h_fov_adjusted"] + v_fov = test_case["v_fov_adjusted"] + required_overlap = params.overlap + pano = test_case["pano"] + min_overlap = 999 + for frame in pano.itertuples(): + # analyze northern hemisphere only to save time. southern is symmetrical. + if frame.tilt < 0: + continue + frame_grid = get_frame_test_grid((frame.pan, frame.tilt), h_fov, v_fov) + neighbors = get_manhattan_neighbors(frame, test_case) + if verbose: + print("Frame %s" % (frame,)) + for dir_label, dir_neighbors in neighbors: + in_dir_neighbors_count = np.zeros( + (frame_grid.shape[0], frame_grid.shape[1]) + ) + for neighbor in dir_neighbors: + in_dir_neighbors_count += get_in_frame( + frame_grid, neighbor, h_fov, v_fov + ) + num_overlap = np.count_nonzero(in_dir_neighbors_count) + num_total = in_dir_neighbors_count.size + overlap = float(num_overlap) / num_total + if overlap < min_overlap: + min_overlap = overlap + min_info = frame, dir_label, dir_neighbors + if verbose: + success = overlap > required_overlap + success_text = "PASS" if success else "FAIL" + print( + " %s overlap=%d%% %s %s" + % ( + success_text, + round(overlap * 100), + dir_label, + ", ".join(map(frame_text, dir_neighbors)), + ) + ) + + print("\nMinimum overlap %s%%, at:" % int(min_overlap * 100)) + + frame, dir_label, dir_neighbors = min_info + print("Frame: ", frame) + print("Neighbors in direction: ", dir_label) + print(" %s" % dir_neighbors) + print() + + global_success = min_overlap > required_overlap + global_success_text = "PASS" if global_success else "FAIL" + print("check_overlap:", global_success_text) + return global_success + + +# debug check_overlap() +def plot_overlap_example(test_case): + pano = test_case["pano"] + h_fov, v_fov = test_case["h_fov_adjusted"], test_case["v_fov_adjusted"] + seq = pano.itertuples() + # arbitrarily select first two frames in the pano + frame = next(seq) + neighbor = next(seq) + frame_grid = get_frame_test_grid((frame.pan, frame.tilt), h_fov, v_fov) + bounds_checker = get_bounds_checker((neighbor.pan, neighbor.tilt), h_fov, v_fov) + in_neighbor = bounds_checker(frame_grid) + plot_coverage_count( + test_case, frame_grid, in_neighbor, "Neighbor image includes point" + ) + plt.title("Example analysis of overlap between neighboring images") + num_overlap = np.count_nonzero(in_neighbor) + num_total = in_neighbor.size + neighbor_overlap = float(num_overlap) / num_total + print("%s / %s (%s%%)" % (num_overlap, num_total, round(neighbor_overlap * 100))) + + +def check_test_case(test_case, verbose=False): + label = test_case["label"] + print("=== check_test_case %s ===" % label) + + plot_pano_seq(test_case, "plot_%s_seq.png" % label) + plot_pano_borders(test_case, "plot_%s_borders.png" % label) + + test_grid = get_global_test_grid(test_case) + coverage_count = get_coverage_count(test_case, test_grid) + plot_hist_integer(coverage_count, "plot_%s_coverage_hist.png" % label) + plot_coverage_count( + test_case, + test_grid, + coverage_count, + "Number of pano images that include test point", + "plot_%s_coverage_count.png" % label, + ) + + success = True + success &= check_complete_coverage(coverage_count) + success &= check_overlap(test_case, verbose) + success_text = "PASS" if success else "FAIL" + print() + print("check_test_case %s: %s" % (label, success_text)) + print() + return success + + +def do_plot_pano(in_csv_path, verbose, case_label): + test_cases = pd.read_csv(in_csv_path) + success = True + if case_label: + test_case = get_test_case(in_csv_path, case_label) + success &= check_test_case(test_case, verbose) + else: + for label in test_cases["label"]: + test_case = get_test_case(in_csv_path, label) + success &= check_test_case(test_case, verbose) + success_text = "PASS" if success else "FAIL" + print() + print("overall do_plot_pano:", success_text) + print() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "in_csv", + nargs="?", + help="input file of test cases", + default="pano_test_cases.csv", + ) + parser.add_argument( + "-v", + "--verbose", + help="print more debug info", + default=False, + action="store_true", + ) + parser.add_argument( + "-c", + "--case", + nargs="?", + help="Process only the specified case, instead of all cases", + ) + + args = parser.parse_args() + level = logging.INFO if args.verbose else logging.WARN + logging.basicConfig(level=level, format="%(message)s") + do_plot_pano(args.in_csv, args.verbose, args.case) diff --git a/astrobee/behaviors/inspection/scripts/survey_offset.bash b/astrobee/behaviors/inspection/scripts/survey_offset.bash new file mode 100755 index 00000000..51b19c86 --- /dev/null +++ b/astrobee/behaviors/inspection/scripts/survey_offset.bash @@ -0,0 +1,57 @@ +#!/bin/bash +# +# Copyright (c) 2021, 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. + +set -e + +# Print the help message (list all the options) +print_help() +{ + echo -e "Usage:" + echo -e "survey_offset.bash [survey_path] [offset x] [offset y] [offset z]" + echo +} + +case $1 in + -h | --help ) print_help + exit + ;; +esac + +if [ "$#" -ne 4 ]; then + echo "** Illegal number of parameters **" + print_help + exit +fi + +in_traj=$1 +out_traj="${in_traj%.*}_out.txt" + +while read -r line; +do + stringarray=($line) + if [ ${stringarray[0]} == "#" ]; then + echo $line >> $out_traj; + continue + fi + + echo "$( echo ${stringarray[0]} $2 | awk '{print $1 + $2}' ) $( echo ${stringarray[1]} $3 | awk '{print $1 + $2}' ) $( echo ${stringarray[2]} $4 | awk '{print $1 + $2}' ) ${stringarray[3]} ${stringarray[4]} ${stringarray[5]} ${stringarray[6]}" >> $out_traj; + + +done < $1 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/scripts/survey_stereo.bash b/astrobee/behaviors/inspection/scripts/survey_stereo.bash new file mode 100755 index 00000000..e368014c --- /dev/null +++ b/astrobee/behaviors/inspection/scripts/survey_stereo.bash @@ -0,0 +1,59 @@ +#!/bin/bash +# +# Copyright (c) 2021, 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. + +set -e + +# Print the help message (list all the options) +print_help() +{ + echo -e "Usage:" + echo -e "survey_stereo.bash [survey_path] [stereo offset x] [stereo offset y] [stereo offset z]" + echo +} + +case $1 in + -h | --help ) print_help + exit + ;; +esac + +if [ "$#" -ne 4 ]; then + echo "** Illegal number of parameters **" + print_help + exit +fi + + +in_traj=$1 +out_traj="${in_traj%.*}_stereo.txt" + +while read -r line; + +do + stringarray=($line) + if [ ${stringarray[0]} == "#" ]; then + echo $line >> $out_traj; + continue + fi + + echo "${stringarray[0]} ${stringarray[1]} ${stringarray[2]} ${stringarray[3]} ${stringarray[4]} ${stringarray[5]} ${stringarray[6]}" >> $out_traj; + echo "$( echo ${stringarray[0]} $2 | awk '{print $1 + $2}' ) $( echo ${stringarray[1]} $3 | awk '{print $1 + $2}' ) $( echo ${stringarray[2]} $4 | awk '{print $1 + $2}' ) ${stringarray[3]} ${stringarray[4]} ${stringarray[5]} ${stringarray[6]}" >> $out_traj; + +done < $1 \ No newline at end of file diff --git a/astrobee/behaviors/inspection/src/anomaly_survey.cc b/astrobee/behaviors/inspection/src/anomaly_survey.cc new file mode 100644 index 00000000..4fa51196 --- /dev/null +++ b/astrobee/behaviors/inspection/src/anomaly_survey.cc @@ -0,0 +1,88 @@ +/* Copyright (c) 2021, 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. + */ + +// Include inspection library header +#include +/** + * \ingroup beh + */ +namespace inspection { +/* + This library generates close-up anomaly surveys given the relevant parameters +*/ + + // This function generates a sorted list based on the max viewing angle and resolution + bool Inspection::GenerateSortedList(geometry_msgs::PoseArray &points) { + geometry_msgs::Pose point; + + // Insert point in the target reference frame + tf2::Quaternion quat_point; + double pan, tilt; + + double dist_resolution = cfg_->Get("distance_resolution"); + double angle_resolution_theta = cfg_->Get("angle_resolution_theta") * M_PI / 180.0; + double angle_resolution_phi = cfg_->Get("angle_resolution_phi") * M_PI / 180.0; + double max_angle = cfg_->Get("max_angle") * M_PI / 180.0; + + double max_distance = cfg_->Get("max_distance"); + double min_distance = cfg_->Get("min_distance"); + double target_distance = cfg_->Get("target_distance"); + + // Go through all the alternative points in preference order + for (double r = 0; (r < max_distance - target_distance) || + (r < target_distance - min_distance); r += dist_resolution) { + for (double theta = 0; theta <= max_angle; theta += angle_resolution_theta) { + for (double phi = 0; phi < 2 * M_PI; phi += angle_resolution_phi) { + // ROS_ERROR_STREAM("r: " << r << " phi: " << phi << " theta: " << theta); + + tilt = theta * sin(phi); + pan = - theta * cos(phi); + quat_point.setRPY(0, tilt, pan); + point.orientation = msg_conversions::tf2_quat_to_ros_quat(quat_point); + + + + if ((target_distance + r < max_distance) && r != 0) { // avoid publishing twice on zero + // Insert point + point.position.x = -(target_distance + r) * cos(theta); + point.position.y = (target_distance + r) * sin(theta) * cos(phi); + point.position.z = (target_distance + r) * sin(theta) * sin(phi); + points.poses.push_back(point); + } + if (target_distance - r > min_distance) { + // Insert point + point.position.x = -(target_distance - r) * cos(theta); + point.position.y = (target_distance - r) * sin(theta) * cos(phi); + point.position.z = (target_distance - r) * sin(theta) * sin(phi); + // tilt = (M_PI - theta) * cos(phi); + // pan = (M_PI - theta) * sin(phi); + // quat_point.setRPY(0, tilt, pan); + // point.orientation = msg_conversions::tf2_quat_to_ros_quat(quat_point); + points.poses.push_back(point); + } + if (theta == 0) + break; + } + } + } + return 0; + } + + +} // namespace inspection diff --git a/astrobee/behaviors/inspection/src/camera_projection.cc b/astrobee/behaviors/inspection/src/camera_projection.cc new file mode 100644 index 00000000..65a151bd --- /dev/null +++ b/astrobee/behaviors/inspection/src/camera_projection.cc @@ -0,0 +1,461 @@ +/* Copyright (c) 2021, 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. + */ + +// Include inspection library header +#include +// TODO(mgouveia): look into this, seems like FrustumPlanes does not have the s parameter +// #include + +#include +#include + +/** + * \ingroup beh + */ +namespace inspection { +/* + This class provides the high-level logic that allows the freeflyer to + define the optimal inspection pose. It evaluates: + + * Visibility constraints + * Keepout and Keepin zones + * Obstacle map + + It returns a vector of possible inspection poses that can be updated + in case the move action fails due to planning or unmapped obstacle. + It also constains functions that allow inspection visualization. +*/ + CameraView::CameraView(std::string cam_name, float f, float n) : f_(f), n_(n) { + cam_name_ = cam_name; + + cfg_cam_.AddFile("cameras.config"); + if (!cfg_cam_.ReadFiles()) + ROS_FATAL("Failed to read config files."); + + // Create a transform buffer to listen for transforms + tf_listener_ = std::shared_ptr( + new tf2_ros::TransformListener(tf_buffer_)); + + Eigen::Matrix3d cam_mat; + + config_reader::ConfigReader::Table camera(&cfg_cam_, cam_name.c_str()); + + // Read in distorted image size. + if (!camera.GetInt("width", &W_)) + fprintf(stderr, "Could not read camera width."); + if (!camera.GetInt("height", &H_)) + fprintf(stderr, "Could not read camera height."); + + config_reader::ConfigReader::Table vector(&camera, "intrinsic_matrix"); + for (int i = 0; i < 9; i++) { + if (!vector.GetReal((i + 1), &cam_mat(i / 3, i % 3))) { + fprintf(stderr, "Failed to read vector intrinsic_matrix."); + break; + } + } + + SetProjectionMatrix(cam_mat); + + // Get relative camera position + try { + tf_body_to_cam_ = tf_buffer_.lookupTransform("body", cam_name_, ros::Time(0), ros::Duration(1.0)); + } catch (tf2::TransformException &ex) { + ROS_ERROR("Failed getting transform: %s", ex.what()); + } + } + + + // Return the Projection Matrix + Eigen::Matrix4d CameraView::GetProjectionMatrix() { + return P_; + } + + // Return the Horizontal Field of View + double CameraView::GetHFOV() { + return 2 * atan(W_ / (2 * fx_)); + } + + // Return the Vertical Field of View + double CameraView::GetVFOV() { + return 2 * atan(H_ / (2 * fy_)); + } + + // Return the Horizontal Field of View + double CameraView::GetH() { + return H_; + } + + // Return the Vertical Field of View + double CameraView::GetW() { + return W_; + } + + // + bool CameraView::GetCamXYFromPoint(const geometry_msgs::Pose robot_pose, const geometry_msgs::Point point, int& x, + int& y) { + // Initialize x,y + x = 0; y = 0; + Eigen::Vector4d p; + p << point.x, + point.y, + point.z, + 1; + tf2::Transform camera_pose = msg_conversions::ros_pose_to_tf2_transform(robot_pose) * + msg_conversions::ros_tf_to_tf2_transform(tf_body_to_cam_.transform); + + // Build the View matrix + Eigen::Quaterniond R(camera_pose.getRotation().w(), + camera_pose.getRotation().x(), + camera_pose.getRotation().y(), + camera_pose.getRotation().z()); // Rotation Matrix Identity + Eigen::Vector3d T(camera_pose.getOrigin().x(), + camera_pose.getOrigin().y(), + camera_pose.getOrigin().z()); // Translation Vector + Eigen::Matrix4d V; // Transformation Matrix + V.setIdentity(); // Identity to make bottom row 0,0,0,1 + V.block<3, 3>(0, 0) = R.normalized().toRotationMatrix();; + V.block<3, 1>(0, 3) = T; + // Transform point + Eigen::Vector4d q = P_ * V.inverse() * p; + + x = static_cast(((q(0) / q(3)) + 1) * W_ / 2); + y = static_cast(((q(1) / q(3)) + 1) * H_ / 2); + + if (q(0) / q(3) < -1 || // the point lies beyond the left border of the screen + q(0) / q(3) > 1 || // the point lies beyond the right border of the screen + q(1) / q(3) < -1 || // the point lies beyond the bottom border of the screen + q(1) / q(3) > 1 || // the point lies beyond the top border of the screen + q(2) / q(3) < -1 || // the point lies beyond the near plane of the camera, + // i.e., the point is behind the camera or too close for the camera to see. + q(2) / q(3) > 1) { // the point lies beyond the far plane of the camera, + // i.e., the point is too far away for the camera to see + if (debug_) { + ROS_DEBUG_STREAM(V.inverse() * p); + ROS_DEBUG_STREAM("VisibilityConstraint T pos" << camera_pose.getOrigin().x() << " " + << camera_pose.getOrigin().y() << " " << camera_pose.getOrigin().z()); + ROS_DEBUG_STREAM("VisibilityConstraint T quat" + << camera_pose.getRotation().w() << " " << camera_pose.getRotation().x() << " " + << camera_pose.getRotation().y() << " " << camera_pose.getRotation().z()); + ROS_DEBUG_STREAM("VisibilityConstraint p " << point.x << " " << point.y << " " << point.z); + ROS_DEBUG_STREAM("VisibilityConstraint q " << q(0) / q(3) << " " << q(1) / q(3) << " " << q(2) / q(3)); + + Eigen::Matrix4d corners_near; + corners_near << 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1; + Eigen::Matrix4d p_near = P_.inverse() * corners_near; + ROS_DEBUG_STREAM("p_near 1 " << p_near(0, 0) / p_near(3, 0) << " " << p_near(1, 0) / p_near(3, 0) << " " + << p_near(2, 0) / p_near(3, 0)); + ROS_DEBUG_STREAM("p_near 2 " << p_near(0, 1) / p_near(3, 1) << " " << p_near(1, 1) / p_near(3, 1) << " " + << p_near(2, 1) / p_near(3, 1)); + ROS_DEBUG_STREAM("p_near 3 " << p_near(0, 2) / p_near(3, 2) << " " << p_near(1, 2) / p_near(3, 2) << " " + << p_near(2, 2) / p_near(3, 2)); + ROS_DEBUG_STREAM("p_near 4 " << p_near(0, 3) / p_near(3, 3) << " " << p_near(1, 3) / p_near(3, 3) << " " + << p_near(2, 3) / p_near(3, 3)); + + Eigen::Matrix4d corners_far; + corners_far << 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; + Eigen::Matrix4d p_far = P_.inverse() * corners_far; + ROS_DEBUG_STREAM("p_far 1 " << p_far(0, 0) / p_far(3, 0) << " " << p_far(1, 0) / p_far(3, 0) << " " + << p_far(2, 0) / p_far(3, 0)); + ROS_DEBUG_STREAM("p_far 2 " << p_far(0, 1) / p_far(3, 1) << " " << p_far(1, 1) / p_far(3, 1) << " " + << p_far(2, 1) / p_far(3, 1)); + ROS_DEBUG_STREAM("p_far 3 " << p_far(0, 2) / p_far(3, 2) << " " << p_far(1, 2) / p_far(3, 2) << " " + << p_far(2, 2) / p_far(3, 2)); + ROS_DEBUG_STREAM("p_far 4 " << p_far(0, 3) / p_far(3, 3) << " " << p_far(1, 3) / p_far(3, 3) << " " + << p_far(2, 3) / p_far(3, 3)); + } + // Eliminate point + return false; + } + + + return true; + } + + + bool CameraView::GetCamXYFromPoint(const geometry_msgs::Point point, int& x, int& y) { + // Initialize x,y + x = 0; y = 0; + // Get current camera position if it's not given + geometry_msgs::TransformStamped robot_pose; + try { + robot_pose = tf_buffer_.lookupTransform("world", "body", ros::Time(0), ros::Duration(1.0)); + } catch (tf2::TransformException &ex) { + ROS_ERROR("Failed getting transform: %s", ex.what()); + return false; + } + return GetCamXYFromPoint(msg_conversions::ros_transform_to_ros_pose(robot_pose.transform), point, x, y); + } + + // Get 3D point from camera pixel location and point cloud + bool CameraView::GetPointFromXYD(const sensor_msgs::PointCloud2 pCloud, const int u, const int v, + geometry_msgs::Point& point) { + // Convert from u (column / width), v (row/height) to position in array + // where X,Y,Z data starts + int arrayPosition = v * pCloud.row_step + u * pCloud.point_step; + + // compute position in array where x,y,z data start + int arrayPosX = arrayPosition + pCloud.fields[0].offset; // X has an offset of 0 + int arrayPosY = arrayPosition + pCloud.fields[1].offset; // Y has an offset of 4 + int arrayPosZ = arrayPosition + pCloud.fields[2].offset; // Z has an offset of 8 + + float X = 0.0; + float Y = 0.0; + float Z = 0.0; + + memcpy(&X, &pCloud.data[arrayPosX], sizeof(float)); + memcpy(&Y, &pCloud.data[arrayPosY], sizeof(float)); + memcpy(&Z, &pCloud.data[arrayPosZ], sizeof(float)); + + // make sure output is valid + if (std::isnan(X) || std::isnan(Y) || std::isnan(Z) + || std::isinf(X) || std::isinf(Y) || std::isinf(Z) || + (X == 0.0 && Y == 0.0 && Z == 0.0)) + return false; + + // put data into the point p + point.x = X; + point.y = Y; + point.z = Z; + + return true; + } + + // Checks if a point is inside a poligon, in this case with 4 sides. + // Explanation of the method and example implementation in: + // https://www.eecs.umich.edu/courses/eecs380/HANDOUTS/PROJ2/InsidePoly.html + bool CameraView::InsideTarget(std::vector vert_x, std::vector vert_y, int test_x, int test_y) { + int i, j; + bool c = false; + int n_vert = 4; + for (i = 0, j = n_vert-1; i < n_vert; j = i++) { + if (((vert_y[i] > test_y) != (vert_y[j] > test_y)) && + (test_x < (vert_x[j] - vert_x[i]) * (test_y - vert_y[i]) / (vert_y[j]-vert_y[i]) + vert_x[i]) ) + c = !c; + } + return c; + } + + // Get the distance from the camera to the target using depth camera information + double CameraView::GetDistanceFromTarget(const geometry_msgs::Pose point, std::string depth_cam_name, double size_x, + double size_y) { + // Create depth cam camera model + CameraView depth_cam(depth_cam_name + "_cam", f_, n_); + depth_cam.debug_ = true; + + // Establish where the corners are in the image + // Be aware that at this point the target has been confirmed to be fully within the selected + // inspection camera, so even if the GetCamXYFromPoint function returns false it just means that it is + // outside view because the fov of the depth camera might be smaller than the fov of the selected + // inspection camera + tf2::Transform target_transform = msg_conversions::ros_pose_to_tf2_transform(point); + tf2::Transform p1, p2, p3, p4; + std::vector vert_x{0, 0, 0, 0}, vert_y{0, 0, 0, 0}; + p1 = target_transform * tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(size_x, size_y, 0)); + if (!depth_cam.GetCamXYFromPoint(msg_conversions::tf2_transform_to_ros_pose(p1).position, vert_x[0], vert_y[0])) { + ROS_WARN_STREAM("Point p1 outside depth cam view"); + } + p2 = target_transform * tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(size_x, -size_y, 0)); + if (!depth_cam.GetCamXYFromPoint(msg_conversions::tf2_transform_to_ros_pose(p2).position, vert_x[1], vert_y[1])) { + ROS_WARN_STREAM("Point p2 outside depth cam view"); + } + p3 = target_transform * tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(-size_x, size_y, 0)); + if (!depth_cam.GetCamXYFromPoint(msg_conversions::tf2_transform_to_ros_pose(p3).position, vert_x[2], vert_y[2])) { + ROS_WARN_STREAM("Point p3 outside depth cam view"); + } + p4 = target_transform * tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(-size_x, -size_y, 0)); + if (!depth_cam.GetCamXYFromPoint(msg_conversions::tf2_transform_to_ros_pose(p4).position, vert_x[3], vert_y[3])) { + ROS_WARN_STREAM("Point p4 outside depth cam view"); + } + + // Debug messages + ROS_DEBUG_STREAM("target p1 " << p1.getOrigin().x() << " " << p1.getOrigin().y() << " " << p1.getOrigin().z()); + ROS_DEBUG_STREAM("target p2 " << p2.getOrigin().x() << " " << p2.getOrigin().y() << " " << p2.getOrigin().z()); + ROS_DEBUG_STREAM("target p3 " << p3.getOrigin().x() << " " << p3.getOrigin().y() << " " << p3.getOrigin().z()); + ROS_DEBUG_STREAM("target p4 " << p4.getOrigin().x() << " " << p4.getOrigin().y() << " " << p4.getOrigin().z()); + + // Get most recent depth message + std::string cam_prefix = TOPIC_HARDWARE_PICOFLEXX_PREFIX; + std::string cam_suffix = TOPIC_HARDWARE_PICOFLEXX_SUFFIX; + boost::shared_ptr msg; + msg = ros::topic::waitForMessage( + cam_prefix + depth_cam_name + cam_suffix, ros::Duration(3.5)); // Wait enought time to accomodate simulation + if (msg == NULL) { + ROS_ERROR_STREAM("No point clound message received from " << depth_cam_name << "_cam" + << " using topic: " << cam_prefix << depth_cam_name + << cam_suffix); + return -1; + } + + // Project target points from depth image to 3D space + calculate average + geometry_msgs::Point new_point; + geometry_msgs::Point sum_point; + int points_counter = 0; + ROS_DEBUG_STREAM("width:" << msg->width << " height:" << msg->height); + for (int depth_cam_x = 0; depth_cam_x < msg->width; ++depth_cam_x) { + for (int depth_cam_y = 0; depth_cam_y < msg->height; ++depth_cam_y) { + if (InsideTarget(vert_x, vert_y, depth_cam_x, depth_cam_y) && + depth_cam.GetPointFromXYD(*msg, depth_cam_x, depth_cam_y, new_point)) { + // Veto point somehow + + ROS_DEBUG_STREAM("u:" << depth_cam_x << " v:" << depth_cam_y << " " << depth_cam_x << "," << depth_cam_y + << ": " << new_point.x << " " << new_point.y << " " << new_point.z); + + points_counter += 1; + sum_point.x += new_point.x; + sum_point.y += new_point.y; + sum_point.z += new_point.z; + } + } + } + if (points_counter == 0) { + ROS_ERROR("No target points in depth cam image"); + return false; + } + ROS_DEBUG_STREAM("Average target using haz cam interception: " << sum_point.x / points_counter << " " + << sum_point.y / points_counter << " " + << sum_point.z / points_counter); + + // Calculate distance between estimated target and camera + geometry_msgs::TransformStamped tf_depth_cam_to_cam; + try { + tf_depth_cam_to_cam = tf_buffer_.lookupTransform(depth_cam_name + "_cam", cam_name_, + ros::Time(0), ros::Duration(1.0)); + } catch (tf2::TransformException &ex) { + ROS_ERROR("Failed getting transform: %s", ex.what()); + return false; + } + + return sqrt((tf_depth_cam_to_cam.transform.translation.z - sum_point.z / points_counter) + * (tf_depth_cam_to_cam.transform.translation.z - sum_point.z / points_counter)); + } + + // Define the projection matrix based on camera parameters using the pinhole model + // Note that this definition considers low distortion + bool CameraView::SetProjectionMatrix(Eigen::Matrix3d cam_mat) { + // Get camera parameters + float s, cx, cy; + + // Read in focal length, optical offset and skew + fx_ = cam_mat(0, 0); + fy_ = cam_mat(1, 1); + s = cam_mat(0, 1); + cx = cam_mat(0, 2); + cy = cam_mat(1, 2); + + // Build projection matrix + P_ << 2 * fx_ / W_, 0, 0, 0, + 2 * s / W_, 2 * fy_ / H_, 0, 0, + 2 * (cx / W_) - 1, 2 * (cy / H_) - 1, -(f_ + n_) / (f_ - n_), 2 * (f_ * n_) / (f_ - n_), + 0, 0, -1, 0; + + return true; + } + + // Draw the camera frustum using a marker array for rviz visualization + void CameraView::DrawCameraFrustum(const geometry_msgs::Pose robot_pose, ros::Publisher &publisher) { + tf2::Transform camera_pose = msg_conversions::ros_pose_to_tf2_transform(robot_pose) * + msg_conversions::ros_tf_to_tf2_transform(tf_body_to_cam_.transform); + + // Build the View matrix + Eigen::Quaterniond R = msg_conversions::ros_to_eigen_quat( + msg_conversions::tf2_quat_to_ros_quat(camera_pose.getRotation())); // Rotation Matrix Identity + Eigen::Vector3d T = msg_conversions::ros_point_to_eigen_vector( + msg_conversions::tf2_transform_to_ros_pose(camera_pose).position); // Translation Vector + Eigen::Matrix4d V; // Transformation Matrix + V.setIdentity(); // Identity to make bottom row 0,0,0,1 + V.block<3, 3>(0, 0) = R.normalized().toRotationMatrix();; + V.block<3, 1>(0, 3) = T; + + Eigen::Matrix4d corners_near; + corners_near << 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1; + Eigen::Matrix4d p_near = V * P_.inverse() * corners_near; + + + Eigen::Matrix4d corners_far; + corners_far << 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1; + Eigen::Matrix4d p_far = V * P_.inverse() * corners_far; + + visualization_msgs::MarkerArray msg_visual; + + // Initialize marker message + visualization_msgs::Marker marker; + visualization_msgs::MarkerArray markers; + // Fill in marker properties + marker.header.frame_id = "world"; + marker.header.stamp = ros::Time::now(); + marker.ns = ""; + marker.type = visualization_msgs::Marker::LINE_LIST; + marker.action = visualization_msgs::Marker::ADD; + // With of the line + marker.scale.x = 0.01; + // Define color + marker.color.r = 0; + marker.color.g = 0; + marker.color.b = 1; + marker.color.a = 1.0; + + marker.id = 0; + + // Add near points + int n = 4; + geometry_msgs::Point p; + for (int i = 0; i < n; ++i) { + p.x = p_near(0, i) / p_near(3, i); + p.y = p_near(1, i) / p_near(3, i); + p.z = p_near(2, i) / p_near(3, i); + marker.points.push_back(p); + + p.x = p_near(0, (i + 1) % n) / p_near(3, (i + 1) % n); + p.y = p_near(1, (i + 1) % n) / p_near(3, (i + 1) % n); + p.z = p_near(2, (i + 1) % n) / p_near(3, (i + 1) % n); + marker.points.push_back(p); + } + // Add far points + for (int i = 0; i < n; ++i) { + p.x = p_far(0, i) / p_far(3, i); + p.y = p_far(1, i) / p_far(3, i); + p.z = p_far(2, i) / p_far(3, i); + marker.points.push_back(p); + + p.x = p_far(0, (i + 1) % n) / p_far(3, (i + 1) % n); + p.y = p_far(1, (i + 1) % n) / p_far(3, (i + 1) % n); + p.z = p_far(2, (i + 1) % n) / p_far(3, (i + 1) % n); + marker.points.push_back(p); + } + // Add conn points + for (int i = 0; i < n; ++i) { + p.x = p_far(0, i) / p_far(3, i); + p.y = p_far(1, i) / p_far(3, i); + p.z = p_far(2, i) / p_far(3, i); + marker.points.push_back(p); + + p.x = p_near(0, i) / p_near(3, i); + p.y = p_near(1, i) / p_near(3, i); + p.z = p_near(2, i) / p_near(3, i); + marker.points.push_back(p); + } + + // Add arrow for visualization + msg_visual.markers.push_back(marker); + + // Publish marker message + publisher.publish(msg_visual); + } + +} // namespace inspection diff --git a/astrobee/behaviors/inspection/src/inspection.cc b/astrobee/behaviors/inspection/src/inspection.cc index 1ada84f7..7f87d356 100644 --- a/astrobee/behaviors/inspection/src/inspection.cc +++ b/astrobee/behaviors/inspection/src/inspection.cc @@ -19,15 +19,16 @@ // Include inspection library header #include -#define PI 3.1415 -#define EPS 1e-5 +#include +#include /** * \ingroup beh */ namespace inspection { /* This class provides the high-level logic that allows the freeflyer to - define the optimal inspection pose. It evaluates: + define the optimal inspection pose. It reads the configurations and + evaluates: * Visibility constraints * Keepout and Keepin zones @@ -38,339 +39,227 @@ namespace inspection { It also constains functions that allow inspection visualization. */ - Inspection::Inspection(ros::NodeHandle* nh, ff_util::ConfigServer* cfg) { - // Setug config readers - cfg_ = cfg; - - cfg_cam_.AddFile("cameras.config"); - if (!cfg_cam_.ReadFiles()) - ROS_FATAL("Failed to read config files."); - - // Create a transform buffer to listen for transforms - tf_listener_ = std::shared_ptr( - new tf2_ros::TransformListener(tf_buffer_)); - // Service clients - // Initialize the zones call - client_z_.SetConnectedCallback(std::bind(&Inspection::ConnectedCallback, this)); - client_z_.SetTimeoutCallback(std::bind(&Inspection::CheckZonesTimeoutCallback, this)); - client_z_.Create(nh, SERVICE_MOBILITY_GET_ZONES); - // Initialize the obstacle map call - client_o_.SetConnectedCallback(std::bind(&Inspection::ConnectedCallback, this)); - client_o_.SetTimeoutCallback(std::bind(&Inspection::CheckMapTimeoutCallback, this)); - client_o_.Create(nh, SERVICE_MOBILITY_GET_OBSTACLE_MAP); - - // Publish - pub_no_filter_ = nh->advertise( - "markers/no_filter", 1, true); - pub_vis_check_ = nh->advertise( - "markers/vis_check", 1, true); - pub_zones_check_ = nh->advertise( - "markers/zones_check", 1, true); - pub_map_check_ = nh->advertise( - "markers/map_check", 1, true); +Inspection::Inspection(ros::NodeHandle* nh, ff_util::ConfigServer* cfg) { + // Setug config readers + cfg_ = cfg; + + // Create a transform buffer to listen for transforms + tf_listener_ = std::shared_ptr(new tf2_ros::TransformListener(tf_buffer_)); + + // Service clients + // Initialize the zones call + client_z_.SetTimeoutCallback(std::bind(&Inspection::CheckZonesTimeoutCallback, this)); + client_z_.Create(nh, SERVICE_MOBILITY_GET_ZONES); + + // Initialize the obstacle map call + client_o_.SetTimeoutCallback(std::bind(&Inspection::CheckMapTimeoutCallback, this)); + client_o_.Create(nh, SERVICE_MOBILITY_GET_OBSTACLE_MAP); + + // Publish debug messages + pub_targets_ = nh->advertise( + TOPIC_BEHAVIORS_INSPECTION_MARKERS + std::string("/targets"), 1, true); + pub_markers_ = nh->advertise( + TOPIC_BEHAVIORS_INSPECTION_MARKERS + std::string("/markers"), 1, true); + pub_cam_ = + nh->advertise(TOPIC_BEHAVIORS_INSPECTION_MARKERS + std::string("/cams"), 1, true); +} + +void Inspection::ReadParam() { + // Parameters Anomaly survey --- + target_size_x_ = cfg_->Get("target_size_x"); + target_size_y_ = cfg_->Get("target_size_y"); + depth_cam_ = cfg_->Get("depth_cam"); + + // Get transform from target to cam + // Camera uses z-axis pointing at the target and the target does z-axis pointing at camera + try { + geometry_msgs::TransformStamped tf_target_to_cam = tf_buffer_.lookupTransform("cam", "target", + ros::Time(0), ros::Duration(1.0)); + target_to_cam_rot_ = tf2::Quaternion( + tf_target_to_cam.transform.rotation.x, + tf_target_to_cam.transform.rotation.y, + tf_target_to_cam.transform.rotation.z, + tf_target_to_cam.transform.rotation.w); + } catch (tf2::TransformException &ex) { + ROS_ERROR("Failed getting target to sci_cam transform: %s", ex.what()); + target_to_cam_rot_ = tf2::Quaternion(0, 0, 0, 1); } - void Inspection::ReadParam() { - // Parameters Anomaly survey - dist_resolution_ = cfg_->Get("distance_resolution"); - angle_resolution_ = cfg_->Get("angle_resolution"); - max_angle_ = cfg_->Get("max_angle"); - max_distance_ = cfg_->Get("max_distance"); - min_distance_ = cfg_->Get("min_distance"); - target_size_x_ = cfg_->Get("target_size_x"); - target_size_y_ = cfg_->Get("target_size_y"); - - // Get transform from target to sci cam - try { - geometry_msgs::TransformStamped tf_target_to_sci_cam = tf_buffer_.lookupTransform("sci_cam", "target", - ros::Time(0)); - opt_distance_ = tf_target_to_sci_cam.transform.translation.z; - target_to_scicam_rot_ = tf2::Quaternion( - tf_target_to_sci_cam.transform.rotation.x, - tf_target_to_sci_cam.transform.rotation.y, - tf_target_to_sci_cam.transform.rotation.z, - tf_target_to_sci_cam.transform.rotation.w); - } catch (tf2::TransformException &ex) { - ROS_ERROR("ERROR getting target to sci_cam transform: %s", ex.what()); - target_to_scicam_rot_ = tf2::Quaternion(0, 0, 0, 1); - } + // Parameters Panorama survey + h_fov_ = cfg_->Get("h_fov") * M_PI / 180.0; + v_fov_ = cfg_->Get("v_fov") * M_PI / 180.0; + pan_min_ = cfg_->Get("pan_min") * M_PI / 180.0; + pan_max_ = cfg_->Get("pan_max") * M_PI / 180.0; + tilt_min_ = cfg_->Get("tilt_min") * M_PI / 180.0; + tilt_max_ = cfg_->Get("tilt_max") * M_PI / 180.0; + att_tol_ = cfg_->Get("att_tol") * M_PI / 180.0; + overlap_ = cfg_->Get("overlap"); +} - // Parameters Panorama survey - pan_min_ = cfg_->Get("pan_min") * PI / 180.0; - pan_max_ = cfg_->Get("pan_max") * PI / 180.0; - tilt_min_ = cfg_->Get("tilt_min") * PI / 180.0; - tilt_max_ = cfg_->Get("tilt_max") * PI / 180.0; - overlap_ = cfg_->Get("overlap"); - } +// Timeout on a zone check request +void Inspection::CheckZonesTimeoutCallback() { + ROS_ERROR("Timeout connecting to the get zones service"); +} - // Ensure all clients are connected - void Inspection::ConnectedCallback() { - ROS_DEBUG_STREAM("ConnectedCallback()"); - if (!client_z_.IsConnected()) return; // Zone check service - if (!client_o_.IsConnected()) return; // Map check service - // fsm_.Update(READY); // Ready! - } +// Timeout on a map check request +void Inspection::CheckMapTimeoutCallback() { + ROS_ERROR("Timeout connecting to the get map service"); +} - // Timeout on a zone check request - void Inspection::CheckZonesTimeoutCallback() { - ROS_ERROR("Timeout connecting to the get zones service"); - } +// In case move failed, remove this point +bool Inspection::RemoveInspectionPose() { + ROS_DEBUG_STREAM("Counter " << inspection_counter_); + points_[inspection_counter_].poses.erase(points_[inspection_counter_].poses.begin()); + if (points_[inspection_counter_].poses.empty()) + return false; + else + return true; +} - // Timeout on a map check request - void Inspection::CheckMapTimeoutCallback() { - ROS_ERROR("Timeout connecting to the get map service"); +// Get the Inspection point on front of possibilities +geometry_msgs::PoseArray Inspection::GetCurrentInspectionPose() { + // Draw camera frostum + if (cameras_.find(curr_camera_) != cameras_.end()) { + cameras_.find(curr_camera_)->second.DrawCameraFrustum(points_[inspection_counter_].poses.front(), pub_cam_); } - bool Inspection::RemoveInspectionPose() { - points_.poses.erase(points_.poses.begin()); + geometry_msgs::PoseArray result; + result.header = points_[inspection_counter_].header; + result.poses.push_back(points_[inspection_counter_].poses.front()); + ROS_DEBUG_STREAM("next pose position x:" << result.poses.front().position.x + << " y:" << result.poses.front().position.y + << " z:" << result.poses.front().position.z + << "quat x:" << result.poses.front().orientation.x + << " y:" << result.poses.front().orientation.y + << " z:" << result.poses.front().orientation.z + << " w:" << result.poses.front().orientation.w); + return result; +} - DrawInspectionPoses(points_, pub_map_check_); - if (points_.poses.empty()) - return false; - else - return true; +// Skip pose +bool Inspection::NextInspectionPose() { + ROS_DEBUG_STREAM("NextInspectionPose " << inspection_counter_); + if (inspection_counter_ + 1 < points_.size()) { + inspection_counter_ += 1; + return true; + } else { + return false; } - - geometry_msgs::Pose Inspection::GetInspectionPose() { - return points_.poses.front(); +} +// Redo pose +bool Inspection::RedoInspectionPose() { + ROS_DEBUG_STREAM("RedoInspectionPose " << inspection_counter_); + if (inspection_counter_ >= 0) { + inspection_counter_ -= 1; + return true; + } else { + return false; } +} +// Get poses +geometry_msgs::PoseArray Inspection::GetInspectionPoses() { + ROS_DEBUG_STREAM("GetInspectionPoses " << inspection_counter_); + geometry_msgs::PoseArray result; + result.header = points_.front().header; + for (int i = inspection_counter_ + 1; i < points_.size(); ++i) { + result.poses.push_back(points_[i].poses.front()); + } + return result; +} - // MOVE ACTION CLIENT - // Generate inspection segment - bool Inspection::GenSegment(geometry_msgs::Pose goal) { - // Update parameters - ReadParam(); - // Insert Offset - tf2::Transform vent_transform; - vent_transform.setOrigin(tf2::Vector3( - goal.position.x, - goal.position.y, - goal.position.z)); - vent_transform.setRotation(tf2::Quaternion( - goal.orientation.x, - goal.orientation.y, - goal.orientation.z, - goal.orientation.w)); - - // Create the sorted point segment - points_.poses.clear(); - points_.header.frame_id = "sci_cam"; - GenerateSortedList(points_); - // ROS_ERROR_STREAM("end GenerateSortedList"); - - // Draw the poses generated that capture the target - if (!VisibilityConstraint(points_)) { - ROS_ERROR_STREAM("Visibility Constrained: Did not find a possible inspection point"); - return false; - } - // DrawInspectionPoses(points_, pub_vis_check_); - - // Transform the points from the camera reference frame to the robot body - TransformList(points_, points_, vent_transform); - - // Check candidate segment agains zones - if (!ZonesConstraint(points_)) { - ROS_ERROR_STREAM("Zones Constrained: Did not find a possible inspection point"); - return false; +double Inspection::GetDistanceToTarget() { + ROS_DEBUG_STREAM("GetDistanceToTarget " << inspection_counter_); + if (mode_ == "anomaly") { + if (cameras_.find(curr_camera_) != cameras_.end()) { + return cameras_.find(curr_camera_)->second.GetDistanceFromTarget(goal_.poses[inspection_counter_], + depth_cam_, target_size_x_, target_size_y_); } - - // Check candidate segment against obstacle map - if (!ObstaclesConstraint(points_)) { - ROS_ERROR_STREAM("Obstacles Constrained: Did not find a possible inspection point"); - return false; - } - // ROS_ERROR_STREAM("end ObstaclesConstraint"); - return true; } + return -1; +} - // This function generates a sorted list based on the max viewing angle and resolution - bool Inspection::GenerateSortedList(geometry_msgs::PoseArray &points) { - geometry_msgs::Pose point; - - // Insert point - point.orientation.x = 1; - point.orientation.y = 0; - point.orientation.z = 0; - point.orientation.w = 0; - - // Go through all the alternative points in preference order - for (double r = 0; (r < max_distance_ - opt_distance_) || - (r < opt_distance_ - min_distance_); r += dist_resolution_) { - for (double theta = 0; theta < max_angle_; theta += angle_resolution_) { - for (double phi = 0; phi < 2*3.14; phi += angle_resolution_) { - // ROS_ERROR_STREAM("r: " << r << " phi: " << phi << " z: " << theta); - if ((opt_distance_ + r < max_distance_) && r != 0) { // avoid publishing twice on zero - // Insert point - point.position.x = (opt_distance_ + r) * sin(theta) * cos(phi); - point.position.y = (opt_distance_ + r) * sin(theta) * sin(phi); - point.position.z = (opt_distance_ + r) * cos(theta); - points_.poses.push_back(point); - // Insert point - if (theta != 0) { // avoid publishing twice on zero - point.position.x = (opt_distance_ + r) * sin(-theta) * cos(phi); - point.position.y = (opt_distance_ + r) * sin(-theta) * sin(phi); - point.position.z = (opt_distance_ + r) * cos(-theta); - points_.poses.push_back(point); - } - } - if (opt_distance_ - r > min_distance_) { - // Insert point - point.position.x = (opt_distance_ - r) * sin(theta) * cos(phi); - point.position.y = (opt_distance_ - r) * sin(theta) * sin(phi); - point.position.z = (opt_distance_ - r) * cos(theta); - points_.poses.push_back(point); - // Insert point - if (theta != 0) { // avoid publishing twice on zero - point.position.x = (opt_distance_ - r) * sin(-theta) * cos(phi); - point.position.y = (opt_distance_ - r) * sin(-theta) * sin(phi); - point.position.z = (opt_distance_ - r) * cos(-theta); - points_.poses.push_back(point); - } - } - if (theta == 0) - break; - } - } +// Checks the given points agains whether the target is visible +// from a camera picture +bool Inspection::VisibilityConstraint(geometry_msgs::PoseArray &points, tf2::Transform target_transform) { + // Go through all the points in sorted segment + std::vector::const_iterator it = points.poses.begin(); + tf2::Transform p1, p2, p3, p4; + ROS_DEBUG_STREAM_ONCE("VisibilityConstraint target " << target_transform.getOrigin().x() + << target_transform.getOrigin().y() + << target_transform.getOrigin().z()); + while (it != points.poses.end()) { + p1 = + target_transform * tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(target_size_x_, target_size_y_, 0)); + ROS_DEBUG_STREAM_ONCE("VisibilityConstraint p1 " << p1.getOrigin().x() << " " << p1.getOrigin().y() << " " + << p1.getOrigin().z()); + p2 = target_transform * + tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(target_size_x_, -target_size_y_, 0)); + ROS_DEBUG_STREAM_ONCE("VisibilityConstraint p2 " << p2.getOrigin().x() << " " << p2.getOrigin().y() << " " + << p2.getOrigin().z()); + p3 = target_transform * + tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(-target_size_x_, target_size_y_, 0)); + ROS_DEBUG_STREAM_ONCE("VisibilityConstraint p3 " << p3.getOrigin().x() << " " << p3.getOrigin().y() << " " + << p3.getOrigin().z()); + p4 = target_transform * + tf2::Transform(tf2::Quaternion(0, 0, 0, 1), tf2::Vector3(-target_size_x_, -target_size_y_, 0)); + ROS_DEBUG_STREAM_ONCE("VisibilityConstraint p4 " << p4.getOrigin().x() << " " << p4.getOrigin().y() << " " + << p4.getOrigin().z()); + + int x, y; + if (cameras_.find(points.header.frame_id) + ->second.GetCamXYFromPoint(*it, msg_conversions::tf2_transform_to_ros_pose(p1).position, x, y) && + cameras_.find(points.header.frame_id) + ->second.GetCamXYFromPoint(*it, msg_conversions::tf2_transform_to_ros_pose(p2).position, x, y) && + cameras_.find(points.header.frame_id) + ->second.GetCamXYFromPoint(*it, msg_conversions::tf2_transform_to_ros_pose(p3).position, x, y) && + cameras_.find(points.header.frame_id) + ->second.GetCamXYFromPoint(*it, msg_conversions::tf2_transform_to_ros_pose(p4).position, x, y)) { + ++it; + } else { + points.poses.erase(it); } - return 0; } - // Checks the given points agains whether the target is visible - // from a camera picture - bool Inspection::VisibilityConstraint(geometry_msgs::PoseArray &points) { - // Get camera parameters - Eigen::Matrix3d cam_mat; - float fx, fy, s, cx, cy; - int W, H; - - config_reader::ConfigReader::Table camera(&cfg_cam_, points.header.frame_id.c_str()); - // Read in distorted image size. - if (!camera.GetInt("width", &W)) - fprintf(stderr, "Could not read camera width."); - if (!camera.GetInt("height", &H)) - fprintf(stderr, "Could not read camera height."); - - config_reader::ConfigReader::Table vector(&camera, "intrinsic_matrix"); - for (int i = 0; i < 9; i++) { - if (!vector.GetReal((i + 1), &cam_mat(i / 3, i % 3))) { - fprintf(stderr, "Failed to read vector intrinsic_matrix."); - break; - } - } - // Read in focal length, optical offset and skew - fx = cam_mat(0, 0); - fy = cam_mat(1, 1); - s = cam_mat(0, 1); - cx = cam_mat(0, 2); - cy = cam_mat(1, 2); - - // Build the matrix with the points to evaluate - Eigen::MatrixXd p(4, 4); - p << target_size_x_, target_size_x_, -target_size_x_, -target_size_x_, - target_size_y_, -target_size_y_, target_size_y_, -target_size_y_, - 0, 0, 0, 0, - 1, 1, 1, 1; - - // Build projection matrix - float farmnear = max_distance_ - min_distance_; - Eigen::Matrix4d P; - P << 2*fx/W, 0, 0, 0, - 2*s/W, 2*fy/H, 0, 0, - 2*(cx/W)-1, 2*(cy/H)-1, max_distance_ / (farmnear), 1, - 0, 0, -min_distance_ * (max_distance_ / (farmnear)), 1; - - // Go through all the points in sorted segment - std::vector::const_iterator it = points.poses.begin(); - while (it != points.poses.end()) { - // Build the View matrix - Eigen::Quaterniond R(1, 0, 0, 0); // Rotation Matrix Identity - Eigen::Vector3d T(it->position.x, it->position.y, it->position.z); // Translation Vector - Eigen::Matrix4d V; // Transformation Matrix - V.setIdentity(); // Identity to make bottom row 0,0,0,1 - V.block<3, 3>(0, 0) = R.normalized().toRotationMatrix();; - V.block<3, 1>(0, 3) = T; - // Transform point - Eigen::MatrixXd q = P * V.inverse() * p; - - bool eliminated = false; - for (int i = 0; i < q.cols(); ++i) { - if (q(0, i)/q(3, i) < -1 || // the point lies beyond the left border of the screen - q(0, i)/q(3, i) > 1 || // the point lies beyond the right border of the screen - q(1, i)/q(3, i) < -1 || // the point lies beyond the bottom border of the screen - q(1, i)/q(3, i) > 1 || // the point lies beyond the top border of the screen - q(2, i)/q(3, i) < -1 || // the point lies beyond the near plane of the camera, - // i.e., the point is behind the camera or too close for the camera to see. - q(2, i)/q(3, i) > 1) { // the point lies beyond the far plane of the camera, - // i.e., the point is too far away for the camera to see - // Eliminate point - points.poses.erase(it); - eliminated = true; - break; - } - } - if (!eliminated) - ++it; - } + // Check if there are any points left + if (points.poses.empty()) + return false; + else + return true; +} - // Check if there are any points left - if (points.poses.empty()) - return false; - else - return true; +// This function transforms the points from the camera rf to the body rf +bool Inspection::TransformList(geometry_msgs::PoseArray points_in, geometry_msgs::PoseArray &points_out, + tf2::Transform target_transform) { + // Get transform from sci cam to body + points_out = points_in; + tf2::Transform sci_cam_to_body; + try { + geometry_msgs::TransformStamped tf_sci_cam_to_body = tf_buffer_.lookupTransform(points_in.header.frame_id, "body", + ros::Time(0), ros::Duration(1.0)); + sci_cam_to_body = msg_conversions::ros_tf_to_tf2_transform(tf_sci_cam_to_body.transform); + } catch (tf2::TransformException &ex) { + ROS_ERROR("Failed getting transform: %s", ex.what()); + sci_cam_to_body.setOrigin(tf2::Vector3(0, 0, 0)); + sci_cam_to_body.setRotation(tf2::Quaternion(0, 0, 0, 1)); } - // This function transforms the points from the camera rf to the body rf - bool Inspection::TransformList(geometry_msgs::PoseArray points_in, geometry_msgs::PoseArray &points_out, - tf2::Transform target_transform) { - // Get transform from sci cam to body - points_out = points_in; - tf2::Transform sci_cam_to_body; - try { - geometry_msgs::TransformStamped tf_sci_cam_to_body = tf_buffer_.lookupTransform(points_in.header.frame_id, "body", - ros::Time(0)); - sci_cam_to_body.setOrigin(tf2::Vector3( - tf_sci_cam_to_body.transform.translation.x, - tf_sci_cam_to_body.transform.translation.y, - tf_sci_cam_to_body.transform.translation.z)); - sci_cam_to_body.setRotation(tf2::Quaternion( - tf_sci_cam_to_body.transform.rotation.x, - tf_sci_cam_to_body.transform.rotation.y, - tf_sci_cam_to_body.transform.rotation.z, - tf_sci_cam_to_body.transform.rotation.w)); - } catch (tf2::TransformException &ex) { - ROS_ERROR("ERROR getting sci_cam transform: %s", ex.what()); - sci_cam_to_body.setOrigin(tf2::Vector3(0, 0, 0)); - sci_cam_to_body.setRotation(tf2::Quaternion(0, 0, 0, 1)); - } - - tf2::Transform target_to_sci_cam; - for (int i = 0; i < points_in.poses.size(); ++i) { - // Convert to tf2 transform - target_to_sci_cam.setOrigin(tf2::Vector3( - points_in.poses[i].position.x, - points_in.poses[i].position.y, - points_in.poses[i].position.z)); - target_to_sci_cam.setRotation(tf2::Quaternion(points_in.poses[i].orientation.x, - points_in.poses[i].orientation.y, - points_in.poses[i].orientation.z, - points_in.poses[i].orientation.w)); - // Convert to body world pose - tf2::Transform robot_pose = target_transform * target_to_sci_cam * sci_cam_to_body; - - // Write back transformed point - points_out.header.frame_id = "world"; - points_out.poses[i].position.x = robot_pose.getOrigin().x(); - points_out.poses[i].position.y = robot_pose.getOrigin().y(); - points_out.poses[i].position.z = robot_pose.getOrigin().z(); - points_out.poses[i].orientation.x = robot_pose.getRotation().x(); - points_out.poses[i].orientation.y = robot_pose.getRotation().y(); - points_out.poses[i].orientation.z = robot_pose.getRotation().z(); - points_out.poses[i].orientation.w = robot_pose.getRotation().w(); - } - return 0; + tf2::Transform target_to_sci_cam; + for (int i = 0; i < points_in.poses.size(); ++i) { + // Convert to tf2 transform + target_to_sci_cam = msg_conversions::ros_pose_to_tf2_transform(points_in.poses[i]); + // Convert to body world pose + tf2::Quaternion rotation = target_to_sci_cam.getRotation() * tf2::Quaternion(0, 0, -1, 0) * target_to_cam_rot_; + target_to_sci_cam.setRotation(rotation); + tf2::Transform robot_pose = target_transform * target_to_sci_cam * sci_cam_to_body; + + // Write back transformed point + points_out.header.frame_id = "world"; + points_out.poses[i] = msg_conversions::tf2_transform_to_ros_pose(robot_pose); } + return 0; +} - // Check if a point is inside a cuboid +// Check if a point is inside a cuboid bool Inspection::PointInsideCuboid(geometry_msgs::Point const& x, geometry_msgs::Vector3 const& cubemin, geometry_msgs::Vector3 const& cubemax) { @@ -384,258 +273,336 @@ bool Inspection::PointInsideCuboid(geometry_msgs::Point const& x, return true; } - bool Inspection::ZonesConstraint(geometry_msgs::PoseArray &points) { - ROS_DEBUG_STREAM("Service zones"); - ff_msgs::GetZones srv; - if (client_z_.Call(srv)) { - // Check each setpoint in the segment against the zones - std::vector::const_iterator it = points.poses.begin(); - while (it != points.poses.end()) { - std::vector::iterator jt; - // We must visit at least one keepin to be valid - bool point_exists_within_keepin = false; - bool point_exists_within_keepout = false; - for (jt = srv.response.zones.begin(); jt != srv.response.zones.end(); jt++) { - if (jt->type == ff_msgs::Zone::KEEPIN) - if (PointInsideCuboid(it->position, jt->min, jt->max)) - point_exists_within_keepin = true; - if (jt->type == ff_msgs::Zone::KEEPOUT) { - if (PointInsideCuboid(it->position, jt->min, jt->max)) { - ROS_DEBUG_STREAM("KEEPOUT violation"); - point_exists_within_keepout = true; - break; - } +bool Inspection::ZonesConstraint(geometry_msgs::PoseArray &points) { + ff_msgs::GetZones srv; + if (client_z_.Call(srv)) { + // Check each setpoint in the segment against the zones + std::vector::const_iterator it = points.poses.begin(); + while (it != points.poses.end()) { + std::vector::iterator jt; + // We must visit at least one keepin to be valid + bool point_exists_within_keepin = false; + bool point_exists_within_keepout = false; + for (jt = srv.response.zones.begin(); jt != srv.response.zones.end(); jt++) { + if (jt->type == ff_msgs::Zone::KEEPIN) { + if (PointInsideCuboid(it->position, jt->min, jt->max)) + point_exists_within_keepin = true; + } + if (jt->type == ff_msgs::Zone::KEEPOUT) { + if (PointInsideCuboid(it->position, jt->min, jt->max)) { + ROS_DEBUG_STREAM("KEEPOUT violation"); + point_exists_within_keepout = true; + break; } } + } + // Check that we are in a keepin + if (!point_exists_within_keepin || point_exists_within_keepout) { + ROS_DEBUG_STREAM("Not within KEEPIN/KEEPOUT zones"); + it = points.poses.erase(it); + } else { + ++it; + } + } + } else { + ROS_ERROR_STREAM("Could not call zones service, is it connected?"); + } + + // Check if there are any points left + if (points.poses.empty()) + return false; + else + return true; +} + +bool Inspection::ObstaclesConstraint(geometry_msgs::PoseArray &points) { + ff_msgs::GetMap srv; + if (client_o_.Call(srv)) { + // Check the received points against the segment + sensor_msgs::PointCloud point_cloud; + sensor_msgs::convertPointCloud2ToPointCloud(srv.response.points, point_cloud); + + // Go through the point cloud of obstacles + for (int i = 0 ; i < point_cloud.points.size(); ++i) { + // Check each setpoint in the segment against the map + std::vector::const_iterator it = points.poses.begin(); + while (it != points.poses.end()) { // Check that we are in a keepin - if (!point_exists_within_keepin || point_exists_within_keepout) { - ROS_DEBUG_STREAM("Not within KEEPIN/KEEPOUT zones"); + if (std::abs(it->position.x - point_cloud.points[i].x) <= srv.response.resolution && + std::abs(it->position.y - point_cloud.points[i].y) <= srv.response.resolution && + std::abs(it->position.z - point_cloud.points[i].z) <= srv.response.resolution) { it = points.poses.erase(it); + break; } else { ++it; } } } - // Check if there are any points left - if (points.poses.empty()) - return false; - else - return true; + } else { + ROS_ERROR_STREAM("Could not call obstacle service, is it connected?"); + } + // Check if there are any points left + if (points.poses.empty()) + return false; + else + return true; +} + +// Checks the given points agains whether the target is visible +// from a camera picture +void Inspection::DrawPoseMarkers(geometry_msgs::PoseArray &points, ros::Publisher &publisher) { + // ROS_ERROR("Entering PubMapData"); + visualization_msgs::MarkerArray msg_visual; + + // Initialize marker message + visualization_msgs::Marker marker; + visualization_msgs::MarkerArray markers; + // Fill in marker properties + marker.header.frame_id = "world"; + marker.header.stamp = ros::Time::now(); + marker.ns = ""; + marker.type = visualization_msgs::Marker::ARROW; + marker.action = visualization_msgs::Marker::ADD; + // Arrow length + marker.scale.x = 0.1; + // Arrow width + marker.scale.y = 0.01; + // Arrow height + marker.scale.z = 0.01; + + for (int i = 0; i < points.poses.size(); ++i) { + // Pivot point + marker.pose.position = points.poses[i].position; + + // Points along the x-axis + marker.id = i * 3; + marker.pose.orientation = points.poses[i].orientation; + // Define color + marker.color.r = 1; + marker.color.g = 0; + marker.color.b = 0; + marker.color.a = 1.0; + // Add arrow for visualization + msg_visual.markers.push_back(marker); + + // Points along the y-axis + marker.id = 1 + i * 3; + marker.pose.orientation = msg_conversions::eigen_to_ros_quat( + msg_conversions::ros_to_eigen_quat(points.poses[i].orientation) * Eigen::Quaterniond(0.707, 0, 0, 0.707)); + // Define color + marker.color.r = 0; + marker.color.g = 1; + marker.color.b = 0; + marker.color.a = 1.0; + // Add arrow for visualization + msg_visual.markers.push_back(marker); + + // Points along the z-axis + marker.id = 2 + i * 3; + marker.pose.orientation = msg_conversions::eigen_to_ros_quat( + msg_conversions::ros_to_eigen_quat(points.poses[i].orientation) * Eigen::Quaterniond(0.707, 0, -0.707, 0)); + // Define color + marker.color.r = 0; + marker.color.g = 0; + marker.color.b = 1; + marker.color.a = 1.0; + // Add arrow for visualization + msg_visual.markers.push_back(marker); } + // Publish marker message + publisher.publish(msg_visual); +} - bool Inspection::ObstaclesConstraint(geometry_msgs::PoseArray &points) { - ff_msgs::GetMap srv; - if (client_o_.Call(srv)) { - // Check the received points against the segment - sensor_msgs::PointCloud point_cloud; - sensor_msgs::convertPointCloud2ToPointCloud(srv.response.points, point_cloud); - - // Go through the point cloud of obstacles - for (int i = 0 ; i < point_cloud.points.size(); ++i) { - // Check each setpoint in the segment against the map - std::vector::const_iterator it = points.poses.begin(); - while (it != points.poses.end()) { - // Check that we are in a keepin - if (std::abs(it->position.x - point_cloud.points[i].x) <= srv.response.resolution && - std::abs(it->position.y - point_cloud.points[i].y) <= srv.response.resolution && - std::abs(it->position.z - point_cloud.points[i].z) <= srv.response.resolution) { - it = points.poses.erase(it); - break; - } else { - ++it; - } - } - } + +// Generate inspection segment +bool Inspection::GenerateAnomalySurvey(geometry_msgs::PoseArray &points_anomaly) { + mode_ = "anomaly"; + inspection_counter_ = -1; + goal_ = points_anomaly; + // Draw pose targets for rviz visualization + DrawPoseMarkers(points_anomaly, pub_targets_); + + // Update parameters + ReadParam(); + // Create the sorted point segment + points_.clear(); + geometry_msgs::PoseArray survey_template; + survey_template.header.frame_id = points_anomaly.header.frame_id; + // Generate Sorted list of inspection candidates for this target + GenerateSortedList(survey_template); + + // Insert Offset + for (int i = 0; i < points_anomaly.poses.size(); ++i) { + tf2::Transform anomaly_transform; + anomaly_transform = msg_conversions::ros_pose_to_tf2_transform(points_anomaly.poses[i]); + + points_.push_back(survey_template); + + // Make sure camera is loaded + std::string cam_name = points_anomaly.header.frame_id.c_str(); + curr_camera_ = cam_name.c_str(); + if (cameras_.find(cam_name) == cameras_.end()) + cameras_.emplace(std::piecewise_construct, std::make_tuple(cam_name), + std::make_tuple(cam_name, cfg_->Get("max_distance"), cfg_->Get("min_distance"))); + + + // Transform the points from the camera reference frame to the robot body + ROS_DEBUG_STREAM("next pose position x:" << points_[i].poses.front().position.x + << " y:" << points_[i].poses.front().position.y + << " z:" << points_[i].poses.front().position.z + << "quat x:" << points_[i].poses.front().orientation.x + << " y:" << points_[i].poses.front().orientation.y + << " z:" << points_[i].poses.front().orientation.z + << " w:" << points_[i].poses.front().orientation.w); + TransformList(points_[i], points_[i], anomaly_transform); + ROS_DEBUG_STREAM("next pose position x:" << points_[i].poses.front().position.x + << " y:" << points_[i].poses.front().position.y + << " z:" << points_[i].poses.front().position.z + << "quat x:" << points_[i].poses.front().orientation.x + << " y:" << points_[i].poses.front().orientation.y + << " z:" << points_[i].poses.front().orientation.z + << " w:" << points_[i].poses.front().orientation.w); + + + DrawPoseMarkers(points_[i], pub_markers_); + + // Draw the poses generated that capture the target + points_[i].header.frame_id = "sci_cam"; + if (!VisibilityConstraint(points_[i], anomaly_transform)) { + ROS_ERROR("Visibility Constrains: Did not find a possible inspection point"); + return false; } + ROS_DEBUG_STREAM("Visibility Constrained"); - // Check if there are any points left - if (points.poses.empty()) + // Check candidate segment agains zones + if (!ZonesConstraint(points_[i])) { + ROS_ERROR_STREAM("Zones Constrains: Did not find a possible inspection point"); return false; - else - return true; - } + } + ROS_DEBUG_STREAM("Zones Constrained"); - // Checks the given points agains whether the target is visible - // from a camera picture - void Inspection::DrawInspectionPoses(geometry_msgs::PoseArray &points, ros::Publisher &publisher) { - // ROS_ERROR("Entering PubMapData"); - visualization_msgs::MarkerArray msg_visual; - - double x[] = {0, 0, 0}; - // Initialize marker message - visualization_msgs::Marker marker; - visualization_msgs::MarkerArray markers; - // Fill in marker properties - marker.header.frame_id = "world"; - marker.header.stamp = ros::Time::now(); - marker.ns = ""; - marker.type = visualization_msgs::Marker::ARROW; - marker.action = visualization_msgs::Marker::ADD; - // Arrow length - marker.scale.x = 0.1; - // Arrow width - marker.scale.y = 0.01; - // Arrow height - marker.scale.z = 0.05; - - for (int i = 0; i < points.poses.size(); ++i) { - marker.id = i; - // Pivot point - marker.pose.position = points.poses[i].position; - // Poits along the x-axis - marker.pose.orientation = points.poses[i].orientation; - // Define color - // marker.color = IntensityMapColor((float) i / (float) points.size(), 1.0); - if (i == 0) { - marker.color.r = 0; - marker.color.g = 1; - marker.color.b = 0; - marker.color.a = 1.0; - } else if (i == 1) { - marker.color.r = 1; - marker.color.g = 0; - marker.color.b = 0; - marker.color.a = 1.0; - } - // Add arrow for visualization - msg_visual.markers.push_back(marker); + // Check candidate segment against obstacle map + if (!ObstaclesConstraint(points_[i])) { + ROS_ERROR_STREAM("Obstacles Constrains: Did not find a possible inspection point"); + return false; } - // Publish marker message - publisher.publish(msg_visual); + ROS_DEBUG_STREAM("Obstacles Constrained"); } + return true; +} - void Inspection::DrawInspectionFrostum() { - } - // Generate the survey for panorama pictures - void Inspection::GeneratePanoramaSurvey(geometry_msgs::PoseArray &points_panorama) { - // Update parameters - ReadParam(); - - geometry_msgs::PoseArray panorama_relative; - geometry_msgs::PoseArray panorama_transformed; - geometry_msgs::PoseArray panorama_survey; - - // Insert point - geometry_msgs::Pose point; - panorama_relative.header.frame_id = points_panorama.header.frame_id.c_str(); - point.position.x = 0.0; - point.position.y = 0.0; - point.position.z = 0.0; - tf2::Quaternion panorama_rotation; - - - // Get camera parameters - Eigen::Matrix3d cam_mat; - float fx, fy; - int W, H; - - // Read in distorted image size. - config_reader::ConfigReader::Table camera(&cfg_cam_, points_panorama.header.frame_id.c_str()); - if (!camera.GetInt("width", &W)) { - ROS_ERROR("Could not read camera width."); - } - if (!camera.GetInt("height", &H)) { - ROS_ERROR("Could not read camera height."); - } - config_reader::ConfigReader::Table vector(&camera, "intrinsic_matrix"); - for (int i = 0; i < 9; i++) { - if (!vector.GetReal((i + 1), &cam_mat(i / 3, i % 3))) { - ROS_ERROR("Failed to read vector intrinsic_matrix."); - break; - } - } - // Read in focal length - fx = cam_mat(0, 0); - fy = cam_mat(1, 1); - - // Calculate field of views - float h_fov = 2 * atan(W / (2 * fx)); - float v_fov = 2 * atan(H / (2 * fy)); - - // Calculate spacing between pictures - double k_pan = (pan_max_ - pan_min_) / std::ceil((pan_max_ - pan_min_) / (h_fov * (1 - overlap_))); - double k_tilt = (tilt_max_ - tilt_min_) / std::ceil((tilt_max_ - tilt_min_) / (v_fov * (1 - overlap_))); - - // Case where max and min is zero - if (std::isnan(k_pan)) k_pan = PI; - if (std::isnan(k_tilt)) k_tilt = PI; - - // If it's a full 360, skip the last one - if (pan_max_ - pan_min_ >= 2*PI) pan_max_-= 2 * EPS; // Go through all the points - - // Generate all the pan/tilt values - int i = 0; - bool top_view = false; - bool bottom_view = false; - for (double pan = pan_min_; pan <= pan_max_ + EPS; pan += k_pan) { - // zig zag through views - if (i%2 == 0) { - for (double tilt = tilt_min_; tilt <= tilt_max_ + EPS; tilt += k_tilt) { - // Avoid taking multiple pictures at top and bottom - if (tilt < -PI/2 + EPS && tilt > -PI/2 - EPS) { - if (bottom_view) {continue;} else {bottom_view = true;} - } - if (tilt < PI/2 + EPS && tilt > PI/2 - EPS) { - if (top_view) {continue;} else {top_view = true;} - } +// Insert here any geometric survey functionality +bool Inspection::GenerateGeometrySurvey(geometry_msgs::PoseArray &points_geometry) { + mode_ = "geometry"; + inspection_counter_ = -1; + goal_ = points_geometry; + // Draw pose targets for rviz visualization + DrawPoseMarkers(points_geometry, pub_targets_); - ROS_DEBUG_STREAM("pan:" << pan * 180 / PI << " tilt:" << tilt * 180 / PI); - panorama_rotation.setRPY(0, tilt, pan); - panorama_rotation = panorama_rotation * tf2::Quaternion(0, 0, -1, 0) * target_to_scicam_rot_; - point.orientation.x = panorama_rotation.x(); - point.orientation.y = panorama_rotation.y(); - point.orientation.z = panorama_rotation.z(); - point.orientation.w = panorama_rotation.w(); - panorama_relative.poses.push_back(point); - } - } else { - for (double tilt = tilt_max_; tilt >= tilt_min_ - EPS; tilt -= k_tilt) { - // Avoid taking multiple pictures at top and bottom - if (tilt < -PI/2 + EPS && tilt > -PI/2 - EPS) { - if (bottom_view) {continue;} else {bottom_view = true;} - } - if (tilt < PI/2 + EPS && tilt > PI/2 - EPS) { - if (top_view) {continue;} else {top_view = true;} - } + points_.clear(); + geometry_msgs::PoseArray pose; + pose.header = points_geometry.header; + pose.poses.push_back(points_geometry.poses[0]); + for (int i = 0; i < points_geometry.poses.size(); ++i) { + pose.poses[0] = points_geometry.poses[i]; + points_.push_back(pose); + } + return true; +} - ROS_DEBUG_STREAM("pan:" << pan * 180 / PI << " tilt:" << tilt * 180 / PI); - panorama_rotation.setRPY(0, tilt, pan); - panorama_rotation = panorama_rotation * tf2::Quaternion(0, 0, -1, 0) * target_to_scicam_rot_; - point.orientation.x = panorama_rotation.x(); - point.orientation.y = panorama_rotation.y(); - point.orientation.z = panorama_rotation.z(); - point.orientation.w = panorama_rotation.w(); - panorama_relative.poses.push_back(point); - // if ((tilt < -PI/2 + EPS && tilt > -PI/2 - EPS) || (tilt < PI/2 + EPS && tilt > PI/2 - EPS)) - // break; - } - } - i++; - } +// Generate the survey for panorama pictures +bool Inspection::GeneratePanoramaSurvey(geometry_msgs::PoseArray &points_panorama) { + mode_ = "panorama"; + inspection_counter_ = -1; + goal_ = points_panorama; + // Draw pose targets for rviz visualization + DrawPoseMarkers(points_panorama, pub_targets_); + points_.clear(); + // Update parameters + ReadParam(); + + // Make sure camera is loaded + std::string cam_name = points_panorama.header.frame_id.c_str(); + curr_camera_ = cam_name.c_str(); + if (cameras_.find(cam_name) == cameras_.end()) + cameras_.emplace(std::piecewise_construct, std::make_tuple(cam_name), + std::make_tuple(cam_name, cfg_->Get("max_distance"), cfg_->Get("min_distance"))); + + geometry_msgs::PoseArray panorama_relative; + geometry_msgs::PoseArray panorama_transformed; + + // Insert point + geometry_msgs::Pose point; + panorama_relative.header.frame_id = cam_name.c_str(); + point.position.x = 0.0; + point.position.y = 0.0; + point.position.z = 0.0; + tf2::Quaternion panorama_rotation; + + // Calculate fields of view + float h_fov, v_fov; + h_fov = (h_fov_ < 0) ? cameras_.find(cam_name)->second.GetHFOV() : h_fov_; + v_fov = (v_fov_ < 0) ? cameras_.find(cam_name)->second.GetVFOV() : v_fov_; + + int nrows, ncols; + std::vector orientations; + + // pano_orientations() doesn't support panos with non-zero center (not needed) + if (pan_min_ != -pan_max_) { + ROS_ERROR_STREAM(" Pan min: " << pan_min_ << " ; Pan max: " << pan_max_ + << "; They are different! Making pan_min = pan_max"); + return false; + } + if (tilt_min_ != -tilt_max_) { + ROS_ERROR_STREAM(" Tilt min: " << tilt_min_ << " ; Tilt max: " << tilt_max_ + << "; They are different! Making tilt_min = tilt_max"); + return false; + } - // Go through all the panorama points - for (int i = 0; i < points_panorama.poses.size(); ++i) { - // Transform the points from the camera reference frame to the robot body + // Generate coverage pattern pan/tilt values + GeneratePanoOrientations(&orientations, &nrows, &ncols, + pan_max_, tilt_max_, + h_fov, v_fov, + overlap_, att_tol_); + + // Go through all the panorama center locations + panorama_relative.poses.clear(); + panorama_relative.poses.push_back(point); + // panorama_relative.poses.resize(1) + for (const auto& point_panorama : points_panorama.poses) { + for (const auto& orient : orientations) { + ROS_DEBUG_STREAM("pan:" << orient.pan * 180 / M_PI << " tilt:" << orient.tilt * 180 / M_PI); + panorama_rotation.setRPY(0, orient.tilt, orient.pan); + panorama_relative.poses[0].orientation = msg_conversions::tf2_quat_to_ros_quat(panorama_rotation); - tf2::Transform panorama_pose; - panorama_pose.setOrigin(tf2::Vector3( - points_panorama.poses[i].position.x, - points_panorama.poses[i].position.y, - points_panorama.poses[i].position.z)); - panorama_pose.setRotation(tf2::Quaternion( - points_panorama.poses[i].orientation.x, - points_panorama.poses[i].orientation.y, - points_panorama.poses[i].orientation.z, - points_panorama.poses[i].orientation.w)); - TransformList(panorama_relative, panorama_transformed, panorama_pose); - - panorama_survey.poses.insert(std::end(panorama_survey.poses), std::begin(panorama_transformed.poses), - std::end(panorama_transformed.poses)); + // Transform the points from the camera reference frame to the robot body + TransformList(panorama_relative, panorama_transformed, + msg_conversions::ros_pose_to_tf2_transform(point_panorama)); + points_.push_back(panorama_transformed); } - points_panorama = panorama_survey; } + return true; +} +// Insert here any volumetric survey functionality +bool Inspection::GenerateVolumetricSurvey(geometry_msgs::PoseArray &points_volume) { + mode_ = "volumetric"; + inspection_counter_ = -1; + goal_ = points_volume; + // Draw pose targets for rviz visualization + DrawPoseMarkers(points_volume, pub_targets_); + points_.clear(); + + geometry_msgs::PoseArray pose; + pose.header = points_volume.header; + pose.poses.push_back(points_volume.poses[0]); + for (int i = 0; i < points_volume.poses.size(); ++i) { + pose.poses[0] = points_volume.poses[i]; + points_.push_back(pose); + } + return true; +} } // namespace inspection diff --git a/astrobee/behaviors/inspection/src/inspection_node.cc b/astrobee/behaviors/inspection/src/inspection_node.cc index 43e8417e..1b744f89 100644 --- a/astrobee/behaviors/inspection/src/inspection_node.cc +++ b/astrobee/behaviors/inspection/src/inspection_node.cc @@ -40,7 +40,7 @@ #include // Software messages -#include +#include #include #include #include @@ -59,6 +59,8 @@ typedef actionlib::SimpleActionServer Server; +#define EPS 1e-5 + /** * \ingroup beh */ @@ -116,85 +118,42 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { fsm_.Add(STATE::WAITING, GOAL_INSPECT | GOAL_UNPAUSE, [this](FSM::Event const& event) -> FSM::State { Dock("UNDOCK"); - return STATE::INIT_INSPECTION; + return STATE::MOVING_TO_APPROACH_POSE; }); // [2] - fsm_.Add(STATE::INIT_INSPECTION, - DOCK_SUCCESS, [this](FSM::Event const& event) -> FSM::State { + fsm_.Add(STATE::MOVING_TO_APPROACH_POSE, + DOCK_SUCCESS | MOTION_SUCCESS | NEXT_INSPECT, [this](FSM::Event const& event) -> FSM::State { + // Check if there is more to inspect + if (!inspection_->NextInspectionPose()) { + // Inspection is over, return + Result(RESPONSE::SUCCESS, "Inspection Over"); + return STATE::WAITING; + } + switch (goal_.command) { - // Vent command + // Anomaly, Geometry or Panorama command case isaac_msgs::InspectionGoal::ANOMALY: - if (inspection_->GenSegment(goal_.inspect_poses.poses[goal_counter_])) { - MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetInspectionPose()); - return STATE::MOVING_TO_APPROACH_POSE; - } else { - Result(RESPONSE::MOTION_APPROACH_FAILED); - return STATE::WAITING; - } - // Geometry or Panorama command case isaac_msgs::InspectionGoal::GEOMETRY: case isaac_msgs::InspectionGoal::PANORAMA: - MoveInspect(ff_msgs::MotionGoal::NOMINAL, goal_.inspect_poses.poses[goal_counter_]); - return STATE::MOVING_TO_APPROACH_POSE; + MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetCurrentInspectionPose()); + return STATE::VISUAL_INSPECTION; // Volumetric command case isaac_msgs::InspectionGoal::VOLUMETRIC: - MoveInspect(ff_msgs::MotionGoal::NOMINAL, goal_.inspect_poses.poses[goal_counter_]); - return STATE::VOL_INSPECTION; + MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetCurrentInspectionPose()); + return STATE::MOVING_TO_APPROACH_POSE; } return STATE::WAITING; }); - fsm_.Add(STATE::INIT_INSPECTION, + fsm_.Add(STATE::MOVING_TO_APPROACH_POSE, DOCK_FAILED, [this](FSM::Event const& event) -> FSM::State { Result(RESPONSE::DOCK_FAILED); return STATE::WAITING; }); // [3] - fsm_.Add(STATE::MOVING_TO_APPROACH_POSE, - MOTION_SUCCESS, [this](FSM::Event const& event) -> FSM::State { - ImageInspect(); - return STATE::VISUAL_INSPECTION; - }); - // [4] fsm_.Add(STATE::VISUAL_INSPECTION, - NEXT_INSPECT, [this](FSM::Event const& event) -> FSM::State { - // After successful inspection, can increment the counter - goal_counter_++; - // There is more to inspect - if (goal_counter_ < goal_.inspect_poses.poses.size()) { - switch (goal_.command) { - // Vent command - case isaac_msgs::InspectionGoal::ANOMALY: - if (inspection_->GenSegment(goal_.inspect_poses.poses[goal_counter_])) { - MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetInspectionPose()); - return STATE::MOVING_TO_APPROACH_POSE; - } else { - Result(RESPONSE::MOTION_APPROACH_FAILED); - return STATE::WAITING; - } - case isaac_msgs::InspectionGoal::GEOMETRY: - case isaac_msgs::InspectionGoal::PANORAMA: - MoveInspect(ff_msgs::MotionGoal::NOMINAL, goal_.inspect_poses.poses[goal_counter_]); - return STATE::MOVING_TO_APPROACH_POSE; - } - } - // Inspection is over, return - Result(RESPONSE::SUCCESS, "Inspection Over"); - return STATE::WAITING; - }); - // [5] - fsm_.Add(STATE::VOL_INSPECTION, MOTION_SUCCESS, [this](FSM::Event const& event) -> FSM::State { - // After successful inspection, can increment the counter - goal_counter_++; - // There is more to inspect - if (goal_counter_ < goal_.inspect_poses.poses.size()) { - MoveInspect(ff_msgs::MotionGoal::NOMINAL, goal_.inspect_poses.poses[goal_counter_]); - return STATE::VOL_INSPECTION; - } - // Inspection is over, return - Result(RESPONSE::SUCCESS, "Inspection Over"); - Dock("DOCK"); - return STATE::RETURN_INSPECTION; + ImageInspect(); + return STATE::MOVING_TO_APPROACH_POSE; }); // [6] fsm_.Add(STATE::RETURN_INSPECTION, @@ -226,16 +185,18 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { break; // Motion in progress case STATE::MOVING_TO_APPROACH_POSE: + // Visual Inspection in progress + case STATE::VISUAL_INSPECTION: + // If in motion make sure you redo current pose + inspection_->RedoInspectionPose(); client_m_.CancelGoal(); break; case STATE::RETURN_INSPECTION: client_m_.CancelGoal(); client_d_.CancelGoal(); break; - // Visual Inspection in progress - case STATE::VISUAL_INSPECTION: - break; } + // After cancellations, wait return STATE::WAITING; }); @@ -265,9 +226,10 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { pub_guest_sci_ = nh->advertise( TOPIC_COMMAND, 1, true); - // Subscribe to the sci camera topic to make sure a picture was taken - sub_sci_cam_ = nh->subscribe(TOPIC_HARDWARE_SCI_CAM + std::string("/compressed"), 1, - &InspectionNode::SciCamCallback, this); + // Subscribe to the sci camera info topic to make sure a picture was taken + sub_sci_cam_info_ = nh->subscribe("/hw/cam_sci_info", 1, + &InspectionNode::SciCamInfoCallback, this); + // Allow the state to be manually set server_set_state_ = nh->advertiseService(SERVICE_BEHAVIORS_INSPECTION_SET_STATE, @@ -321,8 +283,9 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { &InspectionNode::CancelCallback, this)); server_.Create(nh, ACTION_BEHAVIORS_INSPECTION); - // Read maximum number of retryals for a motion action - max_motion_retry_number_= cfg_.Get("max_motion_retry_number"); + // Timer for the sci cam camera + sci_cam_timeout_ = nh_->createTimer(ros::Duration(cfg_.Get("sci_cam_timeout")), + &InspectionNode::SciCamTimeout, this, false, false); // Initiate inspection library inspection_ = new Inspection(nh, &cfg_); @@ -353,7 +316,7 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { // Send a move command - bool MoveInspect(std::string const& mode, geometry_msgs::Pose pose) { + bool MoveInspect(std::string const& mode, geometry_msgs::PoseArray poses) { // Create a new motion goal ff_msgs::MotionGoal goal; goal.command = ff_msgs::MotionGoal::MOVE; @@ -361,38 +324,30 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { // Package up the desired end pose geometry_msgs::PoseStamped inspection_pose; - inspection_pose.pose = pose; inspection_pose.header.stamp = ros::Time::now(); - goal.states.push_back(inspection_pose); - - // Load parameters from config file - std::string planner = cfg_.Get("planner"); - bool coll_check = cfg_.Get("enable_collision_checking"); - - bool replanning = cfg_.Get("enable_replanning"); - int replanning_attempts = cfg_.Get("max_replanning_attempts"); - bool validation = cfg_.Get("enable_validation"); - bool boostrapping = cfg_.Get("enable_bootstrapping"); - bool immediate = cfg_.Get("enable_immediate"); + for (int i = 0; i < poses.poses.size(); ++i) { + inspection_pose.pose = poses.poses[i]; + goal.states.push_back(inspection_pose); + } // Reconfigure the choreographer ff_util::ConfigClient choreographer_cfg(GetPlatformHandle(), NODE_CHOREOGRAPHER); - choreographer_cfg.Set("planner", planner); - choreographer_cfg.Set("enable_collision_checking", coll_check); + choreographer_cfg.Set("planner", cfg_.Get("planner")); + switch (goal_.command) { - // Vent command case isaac_msgs::InspectionGoal::ANOMALY: - choreographer_cfg.Set("enable_faceforward", - cfg_.Get("enable_faceforward_anomaly")); break; + choreographer_cfg.Set("enable_faceforward", cfg_.Get("enable_faceforward_anomaly")); + break; case isaac_msgs::InspectionGoal::GEOMETRY: - choreographer_cfg.Set("enable_faceforward", - cfg_.Get("enable_faceforward_geometry")); break; + choreographer_cfg.Set("enable_faceforward", cfg_.Get("enable_faceforward_geometry")); + break; } - choreographer_cfg.Set("enable_replanning", replanning); - choreographer_cfg.Set("max_replanning_attempts", replanning_attempts); - choreographer_cfg.Set("enable_validation", validation); - choreographer_cfg.Set("enable_bootstrapping", boostrapping); - choreographer_cfg.Set("enable_immediate", immediate); + choreographer_cfg.Set("enable_collision_checking", cfg_.Get("enable_collision_checking")); + choreographer_cfg.Set("enable_replanning", cfg_.Get("enable_replanning")); + choreographer_cfg.Set ("max_replanning_attempts", cfg_.Get ("max_replanning_attempts")); + choreographer_cfg.Set("enable_validation", cfg_.Get("enable_validation")); + choreographer_cfg.Set("enable_bootstrapping", cfg_.Get("enable_bootstrapping")); + choreographer_cfg.Set("enable_immediate", cfg_.Get("enable_immediate")); if (!choreographer_cfg.Reconfigure()) { NODELET_ERROR_STREAM("Failed to reconfigure choreographer"); return false; @@ -411,56 +366,54 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { // Result of a move action void MResultCallback(ff_util::FreeFlyerActionState::Enum result_code, ff_msgs::MotionResultConstPtr const& result) { + // Check for invalid results + if (result == nullptr) { + ROS_ERROR_STREAM("Invalid result received Motion"); + return fsm_.Update(MOTION_FAILED); + } + + // If successful, return Success switch (result_code) { case ff_util::FreeFlyerActionState::SUCCESS: motion_retry_number_ = 0; return fsm_.Update(MOTION_SUCCESS); } - if (result != nullptr) { - switch (result->response) { - case ff_msgs::MotionResult::PLAN_FAILED: - case ff_msgs::MotionResult::VALIDATE_FAILED: - case ff_msgs::MotionResult::OBSTACLE_DETECTED: - case ff_msgs::MotionResult::REPLAN_NOT_ENOUGH_TIME: - case ff_msgs::MotionResult::REPLAN_FAILED: - case ff_msgs::MotionResult::REVALIDATE_FAILED: - case ff_msgs::MotionResult::VIOLATES_KEEP_OUT: - case ff_msgs::MotionResult::VIOLATES_KEEP_IN: - { - // Try to find an alternate inspection position - if (inspection_->RemoveInspectionPose()) { - MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetInspectionPose()); - return; - } else { - ROS_ERROR_STREAM("No inspection pose possible for selected target"); - } - break; + switch (result->response) { + case ff_msgs::MotionResult::PLAN_FAILED: + case ff_msgs::MotionResult::VALIDATE_FAILED: + case ff_msgs::MotionResult::OBSTACLE_DETECTED: + case ff_msgs::MotionResult::REPLAN_NOT_ENOUGH_TIME: + case ff_msgs::MotionResult::REPLAN_FAILED: + case ff_msgs::MotionResult::REVALIDATE_FAILED: + case ff_msgs::MotionResult::VIOLATES_KEEP_OUT: + case ff_msgs::MotionResult::VIOLATES_KEEP_IN: + { + // Try to find an alternate inspection position + ROS_DEBUG_STREAM("Removing inspection pose"); + if (inspection_->RemoveInspectionPose()) { + MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetCurrentInspectionPose()); + return; + } else { + ROS_ERROR_STREAM("No alternative inspection pose possible for current station"); } - case ff_msgs::MotionResult::TOLERANCE_VIOLATION_POSITION: - case ff_msgs::MotionResult::TOLERANCE_VIOLATION_ATTITUDE: - case ff_msgs::MotionResult::TOLERANCE_VIOLATION_VELOCITY: - case ff_msgs::MotionResult::TOLERANCE_VIOLATION_OMEGA: - { // If it fails because of a motion error, retry - if (motion_retry_number_ < max_motion_retry_number_) { - motion_retry_number_++; - switch (goal_.command) { - // Vent command - case isaac_msgs::InspectionGoal::ANOMALY: - MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetInspectionPose()); - break; - case isaac_msgs::InspectionGoal::GEOMETRY: - case isaac_msgs::InspectionGoal::VOLUMETRIC: - MoveInspect(ff_msgs::MotionGoal::NOMINAL, goal_.inspect_poses.poses[goal_counter_]); - break; - } - } + break; + } + case ff_msgs::MotionResult::TOLERANCE_VIOLATION_POSITION_ENDPOINT: + case ff_msgs::MotionResult::TOLERANCE_VIOLATION_POSITION: + case ff_msgs::MotionResult::TOLERANCE_VIOLATION_ATTITUDE: + case ff_msgs::MotionResult::TOLERANCE_VIOLATION_VELOCITY: + case ff_msgs::MotionResult::TOLERANCE_VIOLATION_OMEGA: + { // If it fails because of a motion error, retry + ROS_DEBUG_STREAM("retry?"); + if (motion_retry_number_ < cfg_.Get("max_motion_retry_number")) { + motion_retry_number_++; + MoveInspect(ff_msgs::MotionGoal::NOMINAL, inspection_->GetCurrentInspectionPose()); + return; } } - - ROS_ERROR_STREAM("Motion failed result error: " << result->response); - } else { - ROS_ERROR_STREAM("Invalid result received Motion"); } + ROS_DEBUG_STREAM("Motion failed result error: " << result->response); + return fsm_.Update(MOTION_FAILED); } @@ -502,24 +455,35 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { } // IMG ANALYSIS + void Flashlight(double level) { + ROS_DEBUG_STREAM("Flashlight toggle " << level); + // Toggle flashlight + ff_msgs::CommandArg arg; + std::vector cmd_args; - // Send a move command - bool ImageInspect() { - // Activate image anomaly detector if there are gound communications - if (goal_.command == isaac_msgs::InspectionGoal::ANOMALY && ground_active_) { - // Send goal - isaac_msgs::ImageInspectionGoal goal; - goal.type = isaac_msgs::ImageInspectionGoal::VENT; - client_i_.SendGoal(goal); - ROS_ERROR_STREAM("sent image inspection goal"); - } + // The command sends two strings. The first has the flshlight name, + // and the second the intensity, encoded as json. + arg.data_type = ff_msgs::CommandArg::DATA_TYPE_STRING; + arg.s = "Front"; + cmd_args.push_back(arg); - // Allow image to stabilize - ros::Duration(cfg_.Get("station_time")).sleep(); + arg.data_type = ff_msgs::CommandArg::DATA_TYPE_FLOAT; + arg.f = level; + cmd_args.push_back(arg); - // Signal an imminent sci cam image - sci_cam_req_ = true; + ff_msgs::CommandStamped cmd; + cmd.header.stamp = ros::Time::now(); + cmd.cmd_name = ff_msgs::CommandConstants::CMD_NAME_SET_FLASHLIGHT_BRIGHTNESS; + cmd.cmd_id = "inspection" + std::to_string(ros::Time::now().toSec()); + cmd.cmd_src = "isaac fsw"; + cmd.cmd_origin = "isaac fsw"; + cmd.args = cmd_args; + pub_guest_sci_.publish(cmd); + } + + void SendPicture(double focus_distance) { + ROS_DEBUG_STREAM("Send picture"); // Take picture ff_msgs::CommandArg arg; std::vector cmd_args; @@ -532,45 +496,102 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { cmd_args.push_back(arg); arg.data_type = ff_msgs::CommandArg::DATA_TYPE_STRING; - arg.s = "{\"name\": \"takePicture\"}"; + arg.s = "{\"name\": \"takePicture\", \"haz_dist\": " + std::to_string(focus_distance) +"}"; cmd_args.push_back(arg); ff_msgs::CommandStamped cmd; cmd.header.stamp = ros::Time::now(); cmd.cmd_name = ff_msgs::CommandConstants::CMD_NAME_CUSTOM_GUEST_SCIENCE; cmd.cmd_id = "inspection" + std::to_string(ros::Time::now().toSec()); - cmd.cmd_src = "guest science"; - cmd.cmd_origin = "guest science"; + cmd.cmd_src = "isaac fsw"; + cmd.cmd_origin = "isaac fsw"; cmd.args = cmd_args; - pub_guest_sci_.publish(cmd); - // Timer for the sci cam camera - ros::Timer sci_cam_timeout_ = nh_->createTimer(ros::Duration(5), &InspectionNode::SciCamTimeout, this, true, false); + sci_cam_timeout_.start(); + } + + // Send a move command + bool ImageInspect() { + // Activate image anomaly detector if there are gound communications + if (goal_.command == isaac_msgs::InspectionGoal::ANOMALY && ground_active_) { + // Send goal + isaac_msgs::ImageInspectionGoal goal; + goal.type = isaac_msgs::ImageInspectionGoal::VENT; + client_i_.SendGoal(goal); + } + + // Allow image to stabilize + ros::Duration(cfg_.Get("station_time")).sleep(); + focus_distance_calculated_ = inspection_->GetDistanceToTarget(); + ROS_DEBUG_STREAM("Distance to target: " << focus_distance_calculated_); + focus_distance_current_ = focus_distance_calculated_; + flashlight_intensity_current_ = 0.0; + + // Signal an imminent sci cam image + sci_cam_req_ = sci_cam_req_ + 1; + + // Send the command + SendPicture(focus_distance_calculated_); + return 0; } - void SciCamCallback(const sensor_msgs::CompressedImage::ConstPtr& msg) { + void SciCamInfoCallback(const sensor_msgs::CameraInfo::ConstPtr& msg) { + ROS_DEBUG_STREAM("Got scicam info"); // The sci cam image was received - if (sci_cam_req_) { - sci_cam_req_ = false; + if (sci_cam_req_ != 0) { + // Clear local variables + sci_cam_timeout_.stop(); + sci_cam_req_ = 0; result_.inspection_result.push_back(isaac_msgs::InspectionResult::PIC_ACQUIRED); result_.picture_time.push_back(msg->header.stamp); - - if (goal_.command == isaac_msgs::InspectionGoal::ANOMALY && ground_active_) { - ROS_ERROR_STREAM("wait for result()"); - return; + ros::Duration(cfg_.Get("station_time")).sleep(); + + if (goal_.command == isaac_msgs::InspectionGoal::ANOMALY) { + ROS_DEBUG_STREAM("Scicam picture acquired - Timestamp: " << msg->header.stamp + << ", Focus distance (m): " << focus_distance_current_ + << ", Focal distance : " << 1.6 * std::pow(focus_distance_current_, -1.41) + << ", Flashlight: " << flashlight_intensity_current_); + // If we're iterating flashlight take second picture with it on + if (flashlight_intensity_current_ != cfg_.Get("toggle_flashlight")) { + flashlight_intensity_current_ = cfg_.Get("toggle_flashlight"); + Flashlight(flashlight_intensity_current_); + } else { + // Move on in focus distance iteration + flashlight_intensity_current_ = 0.0; + Flashlight(flashlight_intensity_current_); + if (focus_distance_current_ == focus_distance_calculated_) { + focus_distance_current_ = cfg_.Get("target_distance") - cfg_.Get("focus_distance_range"); + } else if (focus_distance_current_ < + cfg_.Get("target_distance") + cfg_.Get("focus_distance_range") - EPS) { + focus_distance_current_ += cfg_.Get("focus_distance_step"); + } else { + // Finish inspection + return fsm_.Update(NEXT_INSPECT); + } + } + sci_cam_req_ = 1; + SendPicture(focus_distance_current_); + } else { + return fsm_.Update(NEXT_INSPECT); } - ROS_DEBUG_STREAM("Scicam picture acquired " << ros::Time::now()); - return fsm_.Update(NEXT_INSPECT); } return; } void SciCamTimeout(const ros::TimerEvent& event) { + sci_cam_timeout_.stop(); // The sci cam image was not received - return fsm_.Update(INSPECT_FAILED); + if (sci_cam_req_ < cfg_.Get("sci_cam_max_trials")) { + ROS_WARN_STREAM("Scicam didn't repond, resending it again"); + // Send the command + SendPicture(focus_distance_current_); + return; + } else { + return fsm_.Update(INSPECT_FAILED); + } } // Feedback of an action @@ -609,9 +630,8 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { // Skip command case isaac_msgs::InspectionGoal::SKIP: { - if (!goal_.inspect_poses.poses.empty() && goal_counter_ < goal_.inspect_poses.poses.size() - 1) { + if (inspection_->NextInspectionPose()) { // Skip the current pose - goal_counter_++; result.fsm_result = "Skipped pose"; result.response = RESPONSE::SUCCESS; server_.SendResult(ff_util::FreeFlyerActionState::SUCCESS, result); @@ -627,10 +647,7 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { // Repeat last executed step command case isaac_msgs::InspectionGoal::REPEAT: { - if (!goal_.inspect_poses.poses.empty() && goal_counter_ > 0) { - // Go back to the last pose - goal_counter_--; - + if (inspection_->RedoInspectionPose()) { result.fsm_result = "Will repeat last pose"; result.response = RESPONSE::SUCCESS; server_.SendResult(ff_util::FreeFlyerActionState::SUCCESS, result); @@ -645,18 +662,25 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { } // Save command case isaac_msgs::InspectionGoal::SAVE: - if (!goal_.inspect_poses.poses.empty() && goal_counter_ < goal_.inspect_poses.poses.size()) { + geometry_msgs::PoseArray poses = inspection_->GetInspectionPoses(); + if (!poses.poses.empty()) { std::ofstream myfile; std::string path = ros::package::getPath("inspection") + "/resources/current.txt"; myfile.open(path); - for (int i = goal_counter_; i < goal_.inspect_poses.poses.size(); i++) { - myfile << goal_.inspect_poses.poses[i].position.x << " " - << goal_.inspect_poses.poses[i].position.y << " " - << goal_.inspect_poses.poses[i].position.z << " " - << goal_.inspect_poses.poses[i].orientation.x << " " - << goal_.inspect_poses.poses[i].orientation.y << " " - << goal_.inspect_poses.poses[i].orientation.z << " " - << goal_.inspect_poses.poses[i].orientation.w << "\n"; + for (int i = 0; i < poses.poses.size(); i++) { + // Convert quat to RPY for readability + tf2::Quaternion q; + tf2::fromMsg(poses.poses[i].orientation, q); + tf2::Matrix3x3 m(q); + double roll, pitch, yaw; + m.getRPY(roll, pitch, yaw); + + myfile << poses.poses[i].position.x << " " + << poses.poses[i].position.y << " " + << poses.poses[i].position.z << " " + << roll * 180.0 / M_PI << " " + << pitch * 180.0 / M_PI << " " + << yaw * 180.0 / M_PI << "\n"; } myfile.close(); result.fsm_result = "Saved"; @@ -678,7 +702,6 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { result_.anomaly_result.clear(); result_.inspection_result.clear(); result_.picture_time.clear(); - goal_counter_ = 0; ROS_DEBUG_STREAM("RESET COUNTER"); // Check if there is at least one valid inspection pose @@ -693,24 +716,27 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { switch (goal_.command) { // Vent command case isaac_msgs::InspectionGoal::ANOMALY: - NODELET_DEBUG("Received Goal Vent"); - return fsm_.Update(GOAL_INSPECT); + NODELET_DEBUG("Received Goal Anomaly"); + if (inspection_->GenerateAnomalySurvey(goal_.inspect_poses)) + return fsm_.Update(GOAL_INSPECT); break; // Geometry command case isaac_msgs::InspectionGoal::GEOMETRY: NODELET_DEBUG("Received Goal Geometry"); - return fsm_.Update(GOAL_INSPECT); + if (inspection_->GenerateGeometrySurvey(goal_.inspect_poses)) + return fsm_.Update(GOAL_INSPECT); break; // Panorama command case isaac_msgs::InspectionGoal::PANORAMA: - ROS_ERROR("Received Goal Panorama"); - inspection_->GeneratePanoramaSurvey(goal_.inspect_poses); - return fsm_.Update(GOAL_INSPECT); + NODELET_DEBUG("Received Goal Panorama"); + if (inspection_->GeneratePanoramaSurvey(goal_.inspect_poses)) + return fsm_.Update(GOAL_INSPECT); break; // Volumetric command case isaac_msgs::InspectionGoal::VOLUMETRIC: NODELET_DEBUG("Received Goal Volumetric"); - return fsm_.Update(GOAL_INSPECT); + if (inspection_->GenerateVolumetricSurvey(goal_.inspect_poses)) + return fsm_.Update(GOAL_INSPECT); break; // Invalid command default: @@ -722,6 +748,11 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { break; } } + + isaac_msgs::InspectionResult result; + result.fsm_result = "Not a valid goal, could not find feasible inspection plan"; + result.response = RESPONSE::INVALID_COMMAND; + server_.SendResult(ff_util::FreeFlyerActionState::ABORTED, result); } // Complete the current inspection action @@ -801,7 +832,7 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { case STATE::RETURN_INSPECTION: msg.fsm_state = "RETURN_INSPECTION"; break; } - ROS_DEBUG_STREAM("State changed to " << msg.fsm_state); + NODELET_DEBUG_STREAM("State changed to " << msg.fsm_state); // Broadcast the docking state pub_state_.publish(msg); // Send the feedback if needed @@ -811,7 +842,7 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { break; default: { - ROS_DEBUG_STREAM("InspectionFeedback "); + NODELET_DEBUG_STREAM("InspectionFeedback "); isaac_msgs::InspectionFeedback feedback; feedback.state = msg; server_.SendFeedback(feedback); @@ -842,23 +873,32 @@ class InspectionNode : public ff_util::FreeFlyerNodelet { ff_util::ConfigServer cfg_; ros::Publisher pub_state_; ros::Publisher pub_guest_sci_; - ros::Subscriber sub_sci_cam_; + ros::Subscriber sub_sci_cam_info_; ros::ServiceServer server_set_state_; + ros::Timer sci_cam_timeout_; isaac_msgs::InspectionGoal goal_; - int goal_counter_= 0; std::string m_fsm_subevent_, m_fsm_substate_; std::string d_fsm_subevent_, d_fsm_substate_; std::string i_fsm_substate_; isaac_msgs::InspectionResult result_; int motion_retry_number_= 0; - int max_motion_retry_number_ = 0; // Flag to wait for sci camera - bool sci_cam_req_ = false; + int sci_cam_req_ = 0; bool ground_active_ = false; bool sim_mode_ = false; // Inspection library Inspection* inspection_; + + // Picture counters + double focus_distance_calculated_; + double focus_distance_current_; + double flashlight_intensity_current_; + + public: + // This fixes the Eigen aligment issue + // http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; PLUGINLIB_EXPORT_CLASS(inspection::InspectionNode, nodelet::Nodelet); diff --git a/astrobee/behaviors/inspection/src/panorama_survey.cc b/astrobee/behaviors/inspection/src/panorama_survey.cc new file mode 100644 index 00000000..ffdb07c1 --- /dev/null +++ b/astrobee/behaviors/inspection/src/panorama_survey.cc @@ -0,0 +1,358 @@ +/* Copyright (c) 2021, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "inspection/panorama_survey.h" + +#define EPS 1e-5 + + + +/* This library generates panorama surveys given the relevant parameters*/ + +namespace inspection { + + + +void assert_lte(double a, double b, double eps, const std::string& label) { + if (!(a <= b + eps)) { + ROS_ERROR_STREAM("FAIL: at " << label.c_str() << ":" << a << " should be <= " << b << ", within tolerance " << eps); + } +} + +void assert_gte(double a, double b, double eps, const std::string& label) { + if (!(a >= b - eps)) { + ROS_ERROR_STREAM("FAIL: at " << label.c_str() << ":" << a << " should be >= " << b << ", within tolerance " << eps); + } +} + +/* Returns the effective HFOV for an image with tilt centered at @tilt. + * At large tilt values ("high latitude"), effective HFOV is larger + * because the parallels are shorter near the pole. + * + * :param float h_fov: Horizontal field of view of each image (radians). + * :param float v_fov: Vertical field of view of each image (radians). + * :param float tilt: The tilt of the image center (radians). + * :return: The effective HFOV (radians). + */ +double get_h_fov_effective(double h_fov, double v_fov, double tilt) { + double min_abs_theta = fabs(tilt); + /* We could take the worst-case HFOV from either the middle, top, or + * bottom edge of the image, whichever is smallest. This can be + * important to avoid holes with wide-FOV images near the poles and + * minimal overlap, but tends to be overly conservative in other + * cases. That would be delta = 0.5. + * + * In this latest version, we'll take an intermediate approach of + * checking values halfway between the midpoint and the top/bottom, + * delta = 0.25. Empirically, this seems ok in our coverage validator + * test cases. */ + double delta = 0.25; + + min_abs_theta = std::min(min_abs_theta, fabs(tilt - delta * v_fov)); + min_abs_theta = std::min(min_abs_theta, fabs(tilt + delta * v_fov)); + + return h_fov / cos(min_abs_theta); +} + +class LinSpace { + public: + double range_min, range_max; + unsigned int num_pts; + double step; + + LinSpace(double _range_min, double _range_max, unsigned int _num_pts) : + range_min(_range_min), + range_max(_range_max), + num_pts(_num_pts), + step((range_max - range_min) / (num_pts - 1)) + {} + + double at(unsigned int i) { + return range_min + i * step; + } + void write_vector(std::vector* pts_out) { + pts_out->clear(); + double pt = range_min; + for (int i = 0; i < num_pts; i++) { + pts_out->push_back(pt); + pt += step; + } + } + unsigned int find_closest(double val) { + if (val < range_min) return 0; + if (val > range_max) return num_pts - 1; + return lrint((val - range_min) / step); + } +}; + +/* Returns image center coordinates such that images cover the range + * -@range_radius .. +@range_radius with at least the specified + * @overlap. If one image suffices, it will be centered at 0 (and the + * edges of the image will extend beyond the range if it is smaller than + * @fov). If more than one image is needed to cover the range, the + * boundary images will cover exactly to the edges of the specified + * range (modulo @attitude_tolerance) and the images will be evenly + * spaced. + * + * :param range_radius: Images must cover -range_radius .. +range_radius (radians). + * :param fov: Field of view of each image (radians). + * :param overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). + * :param attitude_tolerance: Ensure overlap criterion is met even if relative attitude between a pair of adjacent images, or between an image and the desired pano boundary, is off by at most this much (radians). + * :return: A sequence of orientations of image centers to write to (radians). + */ +LinSpace pano_1d(double range_radius, double fov, + double overlap, double attitude_tolerance) { + double W = range_radius * 2; + + if ((W + 2 * attitude_tolerance - fov) < 0) { + // Special case: Only one image needed. Center it. + return LinSpace(0, 0, 1); + } + + // sufficient overlap criterion: stride <= fov * (1 - overlap) - attitude_tolerance + // (k - 1) * stride + fov = W + 2 * attitude_tolerance + // stride = (W + 2 * attitude_tolerance - fov) / (k - 1) + // (W + 2 * attitude_tolerance - fov) / (k - 1) <= fov * (1 - overlap) - attitude_tolerance + // k - 1 >= (W + 2 * attitude_tolerance - fov) / (fov * (1 - overlap) - attitude_tolerance) + // k >= (W + 2 * attitude_tolerance - fov) / (fov * (1 - overlap) - attitude_tolerance) + 1 + + int k = std::ceil((W + 2 * attitude_tolerance - fov) + / (fov * (1 - overlap) - attitude_tolerance)) + 1; + +#if 1 + // optional sanity checks + + if (k > 1) { + double stride = (W + 2 * attitude_tolerance - fov) / (k - 1); + assert_lte(stride, fov * (1 - overlap) - attitude_tolerance, EPS, "sufficient overlap"); + } + + // check if we have more images than necessary + if (k == 1) { + // obviously need at least one image + } else if (k == 2) { + assert_lte(fov, W + 2 * attitude_tolerance, EPS, "k = 1 is not enough"); + } else { + double stride1 = (W + 2 * attitude_tolerance - fov) / (k - 2); + assert_gte(stride1, fov * (1 - overlap) - attitude_tolerance, EPS, "k is minimized"); + } +#endif + + double min_center = -(range_radius + attitude_tolerance) + fov / 2; + double max_center = -min_center; + return LinSpace(min_center, max_center, k); +} + +/* Returns image center coordinates such that the images cover the full pan + * range -180 .. 180 with at least the specified @overlap between all consecutive images, + * including at the wrap-around, and the image sequence is centered at pan = 0. + * + * :param float fov: Field of view of each image (radians). + * :param float overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). + * :param float attitude_tolerance: Ensure overlap criterion is met even if relative attitude between a pair of adjacent images, or between an image and the desired pano boundary, is off by at most this much (degrees). + * :return: A sequence of orientations of image centers (radians). + */ +LinSpace pano_1d_complete_pan(double fov, double overlap, double attitude_tolerance) { + unsigned int k = std::ceil((2 * M_PI) / (fov * (1 - overlap) - attitude_tolerance)); + double stride = (2 * M_PI / k); + return LinSpace(-M_PI + 0.5 * stride, M_PI - 0.5 * stride, k); +} + +/* Returns image center coordinates for a row of images at tilt value + * @tilt that cover the range -@pan_radius .. +@pan_radius with at least + * the specified @overlap. Special complete wrap-around behavior is + * triggered when @pan_radius is exactly M_PI. + * + * :param pan_radius: Cover pan angle of -pan_radius to +pan_radius (radians). + * :param h_fov: Horizontal field of view of each image (radians). + * :param v_fov: Vertical field of view of each image (radians). + * :param overlap: Minimum required overlap between consecutive images, as a proportion of the image field of view (0 .. 1). + * :param attitude_tolerance: Ensure complete coverage and sufficient overlap even if relative attitude between a pair of adjacent images, or between an image and the desired pano boundary, is off by at most this much (radians). + * :param tilt: The tilt of the image center (radians). + * :return: A sequence of pan orientations of image centers (radians). + */ +LinSpace pano_1d_pan(double pan_radius, + double h_fov, double v_fov, + double overlap, double attitude_tolerance, + double tilt) { + double h_fov_effective = get_h_fov_effective(h_fov, v_fov, tilt); + if (fabs(pan_radius - M_PI) < EPS) { + return pano_1d_complete_pan(h_fov_effective, overlap, attitude_tolerance); + } else { + return pano_1d(pan_radius, h_fov_effective, overlap, attitude_tolerance); + } +} + +void get_orient_lookup(OrientLookupMap* orient_lookup_out, + const std::vector& orientations) { + orient_lookup_out->clear(); + int frame = 0; + for (const auto& orient : orientations) { + orient_lookup_out->insert(std::make_pair(OrientLookupKey(orient.iy, orient.ix), + OrientLookupValue(frame, orient))); + frame++; + } +} + +/* Given panorama parameters specifying desired coverage, write a vector + * of pan/tilt orientations that will achieve the coverage. Note that + * this coverage planner always puts the center of the panorama at pan, + * tilt = 0, 0. But normally these pan/tilt values are interpreted + * relative to a 6-DOF reference pose, so you can use the reference pose + * attitude to point the center of the panorama in whatever direction + * you want. Note that this algorithm can not guarantee it will satisfy + * the complete coverage and overlap requirements due to the fact that + * image coverage areas are warped into non-rectangles in a way that's + * difficult to handle in general. Therefore, you should validate that + * the pano covers the area you want by adding your parameters to + * pano_test_cases.csv, then running test_pano and plot_pano.py. If + * necessary, encourage the planner to add more images by adding some + * extra "warp margin" into plan_attitude_tolerance_degrees while + * leaving alone test_attitude_tolerance_degrees, until the problem is + * resolved. + * + * orientations_out: Write output PanoAttitude sequence here. + * nrows_out: Write number of rows in panorama here. + * ncols_out: Write number of columns in panorama here. + * pan_radius: Cover the pan range -pan_radius .. +pan_radius (radians). + * tilt_radius: Cover the tilt range -tilt_radius .. +tilt_radius (radians). + * h_fov: Horizontal FOV of the imager (radians). + * v_fov: Vertical FOV of the imager (radians). + * overlap: Required proportion of overlap between consecutive image, 0 .. 1 (unitless). + * attitude_tolerance: Try to ensure complete coverage and sufficient overlap even if relative attitude between a pair of adjacent images, or between an image and the desired pano boundary, is off by at most this much (radians). + */ +void GeneratePanoOrientations(std::vector* orientations_out, + int* nrows_out, + int* ncols_out, + double pan_radius, double tilt_radius, + double h_fov, double v_fov, + double overlap, double attitude_tolerance) { + // calculate all image centers + std::vector image_centers; + LinSpace tilt_seq = pano_1d(tilt_radius, v_fov, overlap, attitude_tolerance); + std::vector tilt_vals; + tilt_seq.write_vector(&tilt_vals); + int nrows = tilt_vals.size(); + std::reverse(tilt_vals.begin(), tilt_vals.end()); // order top-to-bottom + std::vector pan_vals; + double min_abs_tilt = fabs(tilt_vals[0]); + int iy = 0; + for (auto tilt : tilt_vals) { + LinSpace pan_seq = pano_1d_pan(pan_radius, h_fov, v_fov, overlap, attitude_tolerance, tilt); + pan_seq.write_vector(&pan_vals); + min_abs_tilt = std::min(min_abs_tilt, fabs(tilt)); + for (auto pan : pan_vals) { + image_centers.push_back(PanoAttitude(pan, tilt, iy, -1)); + } + iy++; + } + + // assign image centers to columns + LinSpace col_centers = pano_1d_pan(pan_radius, + h_fov, v_fov, + overlap, attitude_tolerance, + min_abs_tilt); + unsigned int ncols = col_centers.num_pts; + for (auto& orient : image_centers) { + orient.ix = col_centers.find_closest(orient.pan); + } + + // Do simple greedy column reassignments as needed. This empirically + // reduces large attitude changes. The principle is to look in the + // adjacent row that is nearer to the equator (if there is one), find + // the frame with the closest pan value, and choose the same column it + // did. + OrientLookupMap orient_lookup; + bool changed = true; + while (changed) { + changed = false; + get_orient_lookup(&orient_lookup, image_centers); + + for (auto& orient : image_centers) { + int ny; + if (orient.tilt < 0) { + ny = orient.iy - 1; + } else { + ny = orient.iy + 1; + } + if (!(0 <= ny && ny < nrows)) { + continue; + } + + double min_abs_pan_diff = 999; + int min_ix = -1; + + for (int nx = orient.ix - 1; nx < orient.ix + 2; nx++) { + int nx_mod = (nx + ncols) % ncols; + const auto& nit = orient_lookup.find(OrientLookupKey(ny, nx_mod)); + if (nit != orient_lookup.end()) { + const inspection::PanoAttitude& n_orient = nit->second.second; + if (fabs(n_orient.tilt) > fabs(orient.tilt)) { + // only do reassignment if neighbor row is actually closer to equator + break; + } + double abs_diff0 = fabs(orient.pan - n_orient.pan); + double abs_diff = std::min(abs_diff0, 360 - abs_diff0); + if (abs_diff < min_abs_pan_diff) { + min_abs_pan_diff = abs_diff; + min_ix = n_orient.ix; + } + } + } + if (min_ix != -1 && min_ix != orient.ix) { + orient.ix = min_ix; + changed = true; + } + } + } + + // order images in column-major order; alternate direction top-to-bottom or bottom-to-top + orientations_out->clear(); + std::vector images_ordered; + std::vector col; + for (int ix = 0; ix < ncols; ix++) { + col.clear(); + for (const auto& orient : image_centers) { + // get images in column ix + if (orient.ix == ix) { + col.push_back(orient); + } + } + + // on odd-numbered columns, reverse tilt order, bottom-to-top + if (ix % 2 == 1) { + std::reverse(col.begin(), col.end()); + } + + // append column to output + orientations_out->insert(orientations_out->end(), col.begin(), col.end()); + } + + (*nrows_out) = tilt_vals.size(); + (*ncols_out) = ncols; +} + +} // namespace inspection diff --git a/astrobee/behaviors/inspection/tools/export_panorama.cc b/astrobee/behaviors/inspection/tools/export_panorama.cc index d3b57f65..be12fb4a 100644 --- a/astrobee/behaviors/inspection/tools/export_panorama.cc +++ b/astrobee/behaviors/inspection/tools/export_panorama.cc @@ -28,7 +28,7 @@ // FSW includes #include #include -#include +#include #include // #include #include diff --git a/astrobee/behaviors/inspection/tools/inspection_tool.cc b/astrobee/behaviors/inspection/tools/inspection_tool.cc index 7b9f6ee1..a131ef1a 100644 --- a/astrobee/behaviors/inspection/tools/inspection_tool.cc +++ b/astrobee/behaviors/inspection/tools/inspection_tool.cc @@ -26,8 +26,9 @@ #include // FSW includes +#include #include -#include +#include #include #include #include @@ -50,7 +51,7 @@ #include #include -#define DEG2RAD 3.1415/180.0 +#define DEG2RAD M_PI/180.0 // Robot namespace DEFINE_string(ns, "", "Robot namespace"); @@ -66,19 +67,42 @@ DEFINE_bool(geometry, false, "Send the inspection command"); DEFINE_bool(panorama, false, "Send the inspection command"); DEFINE_bool(volumetric, false, "Send the inspection command"); -// Configurable Parameters +// General parameters DEFINE_string(camera, "sci_cam", "Camera to use"); -DEFINE_double(tilt_max, 90.0, "Panorama: maximum tilt"); -DEFINE_double(tilt_min, -90.0, "Panorama: minimum tilt"); -DEFINE_double(pan_max, 180.0, "Panorama: maximum pan"); + + +// Configurable Parameters anomaly +DEFINE_double(target_distance, 0.3, "Anomaly: desired distance to target (m)"); +DEFINE_double(min_distance, 0.2, "Anomaly: minimum distance to target (m)"); +DEFINE_double(max_distance, 0.7, "Anomaly: maximum distance to target (m)"); +DEFINE_double(max_angle, 40.0, "Anomaly: maximum angle to target (deg)"); +DEFINE_double(target_size_x, 0.05, "Anomaly: target size x - width (m)"); +DEFINE_double(target_size_y, 0.05, "Anomaly: target size y - height (m)"); +DEFINE_string(depth_cam, "haz", "Anomaly: depth cam to be used for distance measurements"); +DEFINE_double(toggle_flashlight, 0.0, "Anomaly: Toggle flashlight 0=OFF 1=MAX"); +DEFINE_double(focus_distance_step, 0.05, "Anomaly: Step to iterate focus distances (m)"); +DEFINE_double(focus_distance_range, 0.0, "Anomaly: Range when iterating focus distances (m)"); + +// Configurable Parameters panorama +DEFINE_string(panorama_mode, "", "Panorama configuration pre-set"); DEFINE_double(pan_min, -180.0, "Panorama: minimum pan"); -DEFINE_double(overlap, 0.5, "Panorama: overlap between images"); +DEFINE_double(pan_max, 180.0, "Panorama: maximum pan"); +DEFINE_double(tilt_min, -90.0, "Panorama: minimum tilt"); +DEFINE_double(tilt_max, 90.0, "Panorama: maximum tilt"); +DEFINE_double(h_fov, -1.0, "Panorama: camera horizontal fov, default -1 uses camera matrix"); +DEFINE_double(v_fov, -1.0, "Panorama: camera vertical fov, default -1 uses camera matrix"); +DEFINE_double(overlap, 0.5, "Panorama: overlap between images"); +DEFINE_double(att_tol, 5.0, "Panorama: attitude tolerance due to mobility"); + +// One pose plans +DEFINE_string(pos, "", "Desired position in cartesian format 'X Y Z' (meters)"); +DEFINE_string(att, "", "Desired attitude in RPY format 'roll pitch yaw' (degrees)"); // Plan files -DEFINE_string(anomaly_poses, "/resources/vent_jpm.txt", "Vent pose list to inspect"); +DEFINE_string(anomaly_poses, "/resources/inspection_iss.txt", "Vent pose list to inspect"); DEFINE_string(geometry_poses, "/resources/survey_bay_6.txt", "Geometry poses list to map"); -DEFINE_string(panorama_poses, "/resources/panorama_jpm.txt", "Panorama poses list to map"); -DEFINE_string(volumetric_poses, "/resources/wifi_jpm.txt", "Wifi poses list to map"); +DEFINE_string(panorama_poses, "/resources/panorama_iss.txt", "Panorama poses list to map"); +DEFINE_string(volumetric_poses, "/resources/volumetric_iss.txt", "Wifi poses list to map"); // Timeout values for action DEFINE_double(connect, 10.0, "Action connect timeout"); @@ -98,14 +122,50 @@ bool has_only_whitespace_or_comments(const std::string & str) { return true; } -void ReadFile(std::string file, isaac_msgs::InspectionGoal &goal) { +// Read inspection poses from given files +bool ReadPanoramaConfig(double* pan_radius_degrees, double* tilt_rad_deg, double* h_fov_deg, double* v_fov_deg, + double* overlap, double* plan_att_tol_deg) { + std::ifstream ifs(std::string(ros::package::getPath("inspection") + "/resources/pano_test_cases.csv").c_str()); + + // Check if file exists + if (!ifs.is_open()) { + std::cout << "Could not open file: " << ros::package::getPath("inspection") + "/resources/pano_test_cases.csv" + << std::endl; + return false; + } + + std::string line; + std::string label; + double test_att_tol_deg; + while (getline(ifs, line)) { + if (has_only_whitespace_or_comments(line)) continue; + std::replace(line.begin(), line.end(), ',', ' '); + line.erase(std::remove(line.begin(), line.end(), '"'), line.end()); + + std::istringstream is(line); + if ((is >> label >> *pan_radius_degrees >> *tilt_rad_deg >> *h_fov_deg >> *v_fov_deg >> *overlap >> + *plan_att_tol_deg >> test_att_tol_deg)) { + if (FLAGS_panorama_mode == label) { + return true; + } + } else { + std::cout << "Ignoring invalid line: " << line << std::endl; + continue; + } + } + std::cout << "Could not find panorama_mode specified" << std::endl; + return false; +} + +// Read inspection poses from given files +geometry_msgs::PoseArray ReadPosesFile(std::string file) { + geometry_msgs::PoseArray poses; geometry_msgs::Pose pose; - goal.inspect_poses.header.frame_id = FLAGS_camera; // Read file geometry std::ifstream ifs((file).c_str()); if (!ifs.is_open()) { std::cout << "Could not open file: " << file << std::endl; - return; + return poses; } std::string line; tf2::Quaternion quat_robot; @@ -117,42 +177,28 @@ void ReadFile(std::string file, isaac_msgs::InspectionGoal &goal) { double euler_roll, euler_pitch, euler_yaw; double quat_x, quat_y, quat_z, quat_w; if ((is >> origin_x >> origin_y >> origin_z >> quat_x >> quat_y >> quat_z >> quat_w)) { - // Position - pose.position.x = origin_x; - pose.position.y = origin_y; - pose.position.z = origin_z; - - // Orientation - pose.orientation.x = quat_x; - pose.orientation.y = quat_y; - pose.orientation.z = quat_z; - pose.orientation.w = quat_w; - goal.inspect_poses.poses.push_back(pose); + quat_robot = tf2::Quaternion(quat_x, quat_y, quat_z, quat_w); } else { std::istringstream is(line); if ((is >> origin_x >> origin_y >> origin_z >> euler_roll >> euler_pitch >> euler_yaw)) { - // Position - pose.position.x = origin_x; - pose.position.y = origin_y; - pose.position.z = origin_z; - - quat_robot.setRPY(euler_roll * DEG2RAD, - euler_pitch * DEG2RAD, - euler_yaw * DEG2RAD); - // Orientation - pose.orientation.x = quat_robot.x(); - pose.orientation.y = quat_robot.y(); - pose.orientation.z = quat_robot.z(); - pose.orientation.w = quat_robot.w(); - goal.inspect_poses.poses.push_back(pose); + quat_robot.setRPY(euler_roll * DEG2RAD, euler_pitch * DEG2RAD, euler_yaw * DEG2RAD); } else { std::cout << "Ignoring invalid line: " << line << std::endl; continue; } } + // Position + pose.position.x = origin_x; + pose.position.y = origin_y; + pose.position.z = origin_z; + // Orientation + pose.orientation = msg_conversions::tf2_quat_to_ros_quat(quat_robot); + // Add pose to array + poses.poses.push_back(pose); } + return poses; } @@ -241,37 +287,22 @@ void SendGoal(ff_util::FreeFlyerActionClient *clie } else if (FLAGS_save) { goal.command = isaac_msgs::InspectionGoal::SAVE; } else if (FLAGS_anomaly) { - // Fill in command type goal.command = isaac_msgs::InspectionGoal::ANOMALY; - - // Read file - // std::cout << "Reading: " << FLAGS_anomaly_poses << std::endl; - ReadFile(path + FLAGS_anomaly_poses, goal); - + path.append(FLAGS_anomaly_poses); } else if (FLAGS_geometry) { - // Fill in command type goal.command = isaac_msgs::InspectionGoal::GEOMETRY; - - // Read file - // std::cout << "Reading: " << FLAGS_geometry_poses << std::endl; - ReadFile(path + FLAGS_geometry_poses, goal); - + path.append(FLAGS_geometry_poses); } else if (FLAGS_panorama) { - // Fill in command type goal.command = isaac_msgs::InspectionGoal::PANORAMA; - - // Read file - // std::cout << "Reading: " << FLAGS_panorama_poses << std::endl; - ReadFile(path + FLAGS_panorama_poses, goal); - + path.append(FLAGS_panorama_poses); } else if (FLAGS_volumetric) { - // Fill in command type goal.command = isaac_msgs::InspectionGoal::VOLUMETRIC; - - // Read file - // std::cout << "Reading: " << FLAGS_volumetric_poses << std::endl; - ReadFile(path + FLAGS_volumetric_poses, goal); + path.append(FLAGS_volumetric_poses); } + // Read file + std::cout << "Reading: " << path << std::endl; + goal.inspect_poses = ReadPosesFile(path); + goal.inspect_poses.header.frame_id = FLAGS_camera; client->SendGoal(goal); } @@ -404,21 +435,63 @@ int main(int argc, char *argv[]) { std::placeholders::_1, std::placeholders::_2)); client.SetConnectedCallback(std::bind(ConnectedCallback, &client)); client.Create(&nh, ACTION_BEHAVIORS_INSPECTION); - // Configure inspection parameters + + // Configure panorama anomaly parameters + if (FLAGS_anomaly) { + ff_util::ConfigClient cfg(&nh, NODE_INSPECTION); + cfg.Set("target_distance", FLAGS_target_distance); + cfg.Set("min_distance", FLAGS_min_distance); + cfg.Set("max_distance", FLAGS_max_distance); + cfg.Set("max_angle", FLAGS_max_angle); + cfg.Set("target_size_x", FLAGS_target_size_x); + cfg.Set("target_size_y", FLAGS_target_size_y); + cfg.Set("depth_cam", FLAGS_depth_cam); + cfg.Set("toggle_flashlight", FLAGS_toggle_flashlight); + cfg.Set("focus_distance_step", FLAGS_focus_distance_step); + cfg.Set("focus_distance_range", FLAGS_focus_distance_range); + if (!cfg.Reconfigure()) { + std::cout << "Could not reconfigure the inspection node " << std::endl; + ros::shutdown(); + } + } + + // Configure panorama inspection parameters if (FLAGS_panorama) { ff_util::ConfigClient cfg(&nh, NODE_INSPECTION); - cfg.Set("pan_min", FLAGS_pan_min); - cfg.Set("pan_max", FLAGS_pan_max); - cfg.Set("tilt_min", FLAGS_tilt_min); - cfg.Set("tilt_max", FLAGS_tilt_max); - cfg.Set("overlap", FLAGS_overlap); + + if (FLAGS_panorama_mode == "") { + cfg.Set("h_fov", FLAGS_h_fov); + cfg.Set("v_fov", FLAGS_v_fov); + + cfg.Set("pan_min", FLAGS_pan_min); + cfg.Set("pan_max", FLAGS_pan_max); + cfg.Set("tilt_min", FLAGS_tilt_min); + cfg.Set("tilt_max", FLAGS_tilt_max); + cfg.Set("overlap", FLAGS_overlap); + cfg.Set("att_tol", FLAGS_att_tol); + } else { + // Read file panorama config + double pan_radius_degrees, tilt_rad_deg, h_fov_deg, v_fov_deg, overlap, plan_att_tol_deg; + if (ReadPanoramaConfig(&pan_radius_degrees, &tilt_rad_deg, &h_fov_deg, &v_fov_deg, &overlap, &plan_att_tol_deg)) { + cfg.Set("h_fov", h_fov_deg); + cfg.Set("v_fov", v_fov_deg); + + cfg.Set("pan_min", -pan_radius_degrees); + cfg.Set("pan_max", pan_radius_degrees); + cfg.Set("tilt_min", -tilt_rad_deg); + cfg.Set("tilt_max", tilt_rad_deg); + cfg.Set("overlap", overlap); + cfg.Set("att_tol", plan_att_tol_deg); + } + } + if (!cfg.Reconfigure()) { std::cout << "Could not reconfigure the inspection node " << std::endl; ros::shutdown(); } } -std::cout << "\r " + std::cout << "\r " << "Available actions:\n" << "0) Exit \n" << "1) Pause \n" diff --git a/astrobee/behaviors/inspection/tools/sci_cam_tool.cc b/astrobee/behaviors/inspection/tools/sci_cam_tool.cc index 0e5336b2..febe3917 100644 --- a/astrobee/behaviors/inspection/tools/sci_cam_tool.cc +++ b/astrobee/behaviors/inspection/tools/sci_cam_tool.cc @@ -26,7 +26,7 @@ // FSW includes #include -#include +#include #include #include #include diff --git a/astrobee/behaviors/inspection/tools/test_pano.cc b/astrobee/behaviors/inspection/tools/test_pano.cc new file mode 100644 index 00000000..10e41ff2 --- /dev/null +++ b/astrobee/behaviors/inspection/tools/test_pano.cc @@ -0,0 +1,160 @@ +/* Copyright (c) 2021, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "inspection/panorama_survey.h" + +typedef decltype(&inspection::GeneratePanoOrientations) orientations_func_t; + +struct TestCase { + std::string label; + double pan_radius_degrees, tilt_radius_degrees; + double h_fov_degrees, v_fov_degrees; + double overlap; + double plan_attitude_tolerance_degrees; + double test_attitude_tolerance_degrees; +}; + +std::string read_string(std::istream* in) { + std::string result; + std::getline(*in, result, ','); + return result.substr(1, result.size() - 2); +} + +double read_double(std::istream* in) { + std::string s; + std::getline(*in, s, ','); + return std::stod(s); +} + +TestCase read_test_case(std::istream* in) { + TestCase result; + result.label = read_string(in); + result.pan_radius_degrees = read_double(in); + result.tilt_radius_degrees = read_double(in); + result.h_fov_degrees = read_double(in); + result.v_fov_degrees = read_double(in); + result.overlap = read_double(in); + result.plan_attitude_tolerance_degrees = read_double(in); + result.test_attitude_tolerance_degrees = read_double(in); + return result; +} + +void write_pano_csv(const std::string& label, + const std::vector& orientations) { + std::string out_path("case_" + label + ".csv"); + std::ofstream out_csv(out_path); + if (!out_csv) { + std::cerr << "couldn't open " << out_path << " for writing" << std::endl; + } + out_csv << "\"pan\",\"tilt\",\"iy\",\"ix\"" << std::endl; + + for (const auto& orient : orientations) { + out_csv << DEG_FROM_RAD(orient.pan) << "," + << DEG_FROM_RAD(orient.tilt) << "," + << orient.iy << "," + << orient.ix << std::endl; + } + + out_csv.close(); +} + +void print_pano(const std::string& label, + const std::vector& orientations, + int nrows, int ncols) { + std::cout << label << ": "; + printf("%lu images, %d rows x %d cols, frame# [pan tilt]:\n", orientations.size(), nrows, ncols); + + // build lookup + inspection::OrientLookupMap orient_lookup; + inspection::get_orient_lookup(&orient_lookup, orientations); + + // print table format + for (int iy = 0; iy < nrows; iy++) { + for (int ix = 0; ix < ncols; ix++) { + const auto& it = orient_lookup.find(inspection::OrientLookupKey(iy, ix)); + if (it == orient_lookup.end()) { + printf(" "); + } else { + auto& orient_pair = it->second; + int frame = orient_pair.first; + const inspection::PanoAttitude& orient = orient_pair.second; + printf("%3d [%4ld %4ld] ", + frame, + lrintf(DEG_FROM_RAD(orient.pan)), + lrintf(DEG_FROM_RAD(orient.tilt))); + } + } + std::cout << std::endl; + } + + std::cout << std::endl; +} + +void do_test_case(orientations_func_t orientations_func, const TestCase& test_case) { + int nrows, ncols; + std::vector orientations; + orientations_func(&orientations, &nrows, &ncols, + RAD_FROM_DEG(test_case.pan_radius_degrees), + RAD_FROM_DEG(test_case.tilt_radius_degrees), + RAD_FROM_DEG(test_case.h_fov_degrees), + RAD_FROM_DEG(test_case.v_fov_degrees), + test_case.overlap, + RAD_FROM_DEG(test_case.plan_attitude_tolerance_degrees)); + + write_pano_csv(test_case.label, orientations); + print_pano(test_case.label, orientations, nrows, ncols); +} + +void do_test_cases(const std::string& label, orientations_func_t orientations_func) { + std::cout << label << std::endl << std::endl; + + std::string csv_path("pano_test_cases.csv"); + std::ifstream csv_stream(csv_path); + if (!csv_stream) { + std::cerr << "couldn't open " << csv_path << " for reading" << std::endl; + return; + } + + std::string line; + std::getline(csv_stream, line); // ignore header row + while (!csv_stream.eof()) { + std::getline(csv_stream, line); + if (std::string::npos == line.find_first_not_of(" \n")) { + break; + } + std::istringstream row(line); + TestCase test_case = read_test_case(&row); + do_test_case(orientations_func, test_case); + } + + csv_stream.close(); +} + +int main(int argc, char* argv[]) { + do_test_cases("=== GeneratePanoOrientations === ", &inspection::GeneratePanoOrientations); + + return 0; +} diff --git a/astrobee/behaviors/readme.md b/astrobee/behaviors/readme.md index 8de60c00..52ff353b 100644 --- a/astrobee/behaviors/readme.md +++ b/astrobee/behaviors/readme.md @@ -2,5 +2,9 @@ Behaviors are high-level actions that support complex maneuvers, such as control of the arm, docking, and perching. They are built upon the more more generic actions offered by other subsystems, such as the mobility subsystem's "motion" action, or the localization manager's "switch" action. -\subpage inspection -\subpage cargo \ No newline at end of file +Inspection + +\subpage inspection : Coordinates plan making and mobility for ISAAC specific inspection modes such as geometry surveying, panorama taking, anomaly detection and volumetric inspection. + +Cargo +\subpage cargo : Coordinates cargo behaviours such as picking up cargo and dropping off cargo. \ No newline at end of file diff --git a/astrobee/simulation/isaac_gazebo/CMakeLists.txt b/astrobee/simulation/isaac_gazebo/CMakeLists.txt index edd87bf2..cf5e0ad6 100644 --- a/astrobee/simulation/isaac_gazebo/CMakeLists.txt +++ b/astrobee/simulation/isaac_gazebo/CMakeLists.txt @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. -cmake_minimum_required(VERSION 2.8.3) +cmake_minimum_required(VERSION 3.5) project(isaac_gazebo) # Specify C++14 standard @@ -40,6 +40,9 @@ find_package(catkin REQUIRED COMPONENTS find_package(gazebo REQUIRED) +# Add gazebo to the link directorries +link_directories(${GAZEBO_LIBRARY_DIRS}) + # Call catkin catkin_package( INCLUDE_DIRS diff --git a/astrobee/simulation/isaac_gazebo/launch/spawn_object.launch b/astrobee/simulation/isaac_gazebo/launch/spawn_object.launch index e2f889f5..d487526a 100644 --- a/astrobee/simulation/isaac_gazebo/launch/spawn_object.launch +++ b/astrobee/simulation/isaac_gazebo/launch/spawn_object.launch @@ -19,7 +19,7 @@ - + - + name="glp_anomaly" output="$(arg output)"/> @@ -65,7 +63,7 @@ - + diff --git a/isaac/launch/robot/ILP.launch b/isaac/launch/robot/ILP.launch index 544fcc24..6e761d51 100644 --- a/isaac/launch/robot/ILP.launch +++ b/isaac/launch/robot/ILP.launch @@ -19,16 +19,16 @@ - - - - - - - - + + + + + + + + - + @@ -57,7 +57,7 @@ - + @@ -90,7 +90,7 @@ - + diff --git a/isaac/resources/rviz/granite.rviz b/isaac/resources/rviz/granite.rviz index 349d2c87..d54843fe 100644 --- a/isaac/resources/rviz/granite.rviz +++ b/isaac/resources/rviz/granite.rviz @@ -8,7 +8,7 @@ Panels: - /Debug1/Sensors1 - "/Debug1/Sensors1/DEBUG: NavCam1" Splitter Ratio: 0.579250693 - Tree Height: 216 + Tree Height: 224 - Class: rviz/Selection Name: Selection - Class: rviz/Tool Properties @@ -27,7 +27,9 @@ Panels: Experimental: false Name: Time SyncMode: 0 - SyncSource: Features + SyncSource: "" +Toolbars: + toolButtonStyle: 2 Visualization Manager: Class: "" Displays: @@ -79,10 +81,40 @@ Visualization Manager: Frame Timeout: 15 Frames: All Enabled: false + RFID1: + Value: true + RFID1/body: + Value: true + RFID2: + Value: true + RFID2/body: + Value: true + RFID3: + Value: true + RFID3/body: + Value: true ar_tag: Value: true body: Value: true + cam: + Value: true + cargo/approach: + Value: true + cargo/berth: + Value: true + cargo/body: + Value: true + cargo/complete: + Value: true + cargo_goal/approach: + Value: true + cargo_goal/berth: + Value: true + cargo_goal/body: + Value: true + cargo_goal/complete: + Value: true carriage: Value: true dock/berth1: @@ -133,10 +165,18 @@ Visualization Manager: Value: true payload/top_front: Value: true + perch/body: + Value: true perch_cam: Value: true rviz: Value: true + sci_cam: + Value: true + soundsee: + Value: true + target: + Value: true top_aft: Value: true top_aft_arm_distal_link: @@ -153,6 +193,14 @@ Visualization Manager: Value: true truth: Value: true + wifi1/body: + Value: true + wifi2/body: + Value: true + wifi3/body: + Value: true + wifi_receiver: + Value: true world: Value: true Marker Scale: 0.300000012 @@ -162,6 +210,18 @@ Visualization Manager: Show Names: true Tree: world: + RFID1: + {} + RFID1/body: + {} + RFID2: + {} + RFID2/body: + {} + RFID3: + {} + RFID3/body: + {} body: ar_tag: {} @@ -193,6 +253,10 @@ Visualization Manager: {} perch_cam: {} + sci_cam: + {} + soundsee: + {} top_aft: top_aft_arm_proximal_link: top_aft_arm_distal_link: @@ -202,6 +266,8 @@ Visualization Manager: top_aft_gripper_right_proximal_link: top_aft_gripper_right_distal_link: {} + wifi_receiver: + {} dock/body: dock/berth1: dock/berth1/approach: @@ -215,17 +281,20 @@ Visualization Manager: {} granite/body: {} - handrail/body: - handrail/approach: - {} - handrail/complete: - {} iss/body: {} + perch/body: + {} rviz: {} truth: {} + wifi1/body: + {} + wifi2/body: + {} + wifi3/body: + {} Update Interval: 0 Value: true - Alpha: 1 @@ -626,7 +695,7 @@ Visualization Manager: Marker Topic: /mob/mapper/obstacle_markers Name: Map Namespaces: - obstacleMap: true + {} Queue Size: 100 Value: true - Class: rviz/MarkerArray @@ -641,6 +710,22 @@ Visualization Manager: Name: Mobility Enabled: true Name: Debug + - Class: rviz/MarkerArray + Enabled: true + Marker Topic: /beh/inspection/markers/cams + Name: Inspection camera + Namespaces: + {} + Queue Size: 100 + Value: true + - Class: rviz/MarkerArray + Enabled: true + Marker Topic: /beh/inspection/markers/targets + Name: Inspection targets + Namespaces: + {} + Queue Size: 100 + Value: true Enabled: true Global Options: Background Color: 0; 0; 0 @@ -696,7 +781,7 @@ Window Geometry: Height: 884 Hide Left Dock: false Hide Right Dock: false - QMainWindow State: 000000ff00000000fd00000004000000000000016a00000317fc0200000011fb0000001a00440045004200550047003a0020004e0061007600430061006d01000000410000010a0000001600fffffffb0000001c00440045004200550047003a00200044006f0063006b00430061006d0100000151000000e80000001600fffffffb000000100044006900730070006c006100790073010000023f00000119000000e300fffffffb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006700fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000002ac0000026500000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006500000002af000000f40000000000000000fb0000000a0049006d00610067006502000000c60000001d0000052f000003f4fb0000000a0049006d006100670065020000016600000075000004ab00000373fb0000000a0049006d00610067006501000004680000014d0000000000000000fb0000000a0049006d0061006700650000000028000003dd0000000000000000fb000000100044006f0063006b002000430061006d0100000143000001090000000000000000fb0000000e004e00610076002000430061006d010000019d000000af0000000000000000000000010000016a00000455fc0200000004fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0049006d00610067006500000000330000029b0000000000000000fb0000000a00560069006500770073000000002800000396000000b300fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b20000000000000000000000020000078000000115fc0100000001fb0000000a00560069006500770073030000004e00000080000002e1000001970000000300000780000000fffc0100000002fc00000000000004380000000000fffffffa000000000200000002fb0000001e004d006f00740069006f006e00200050006c0061006e006e0069006e00670100000000ffffffff0000000000000000fb0000000800540069006d00650000000000ffffffff0000003900fffffffb0000000800540069006d00650100000000000004500000000000000000000002c90000031700000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + QMainWindow State: 000000ff00000000fd00000004000000000000016a0000032efc0200000011fb0000001a00440045004200550047003a0020004e0061007600430061006d0100000028000001120000001600fffffffb0000001c00440045004200550047003a00200044006f0063006b00430061006d0100000140000000ef0000001600fffffffb000000100044006900730070006c006100790073010000023500000121000000d700fffffffb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006100fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000002ac0000026500000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006500000002af000000f40000000000000000fb0000000a0049006d00610067006502000000c60000001d0000052f000003f4fb0000000a0049006d006100670065020000016600000075000004ab00000373fb0000000a0049006d00610067006501000004680000014d0000000000000000fb0000000a0049006d0061006700650000000028000003dd0000000000000000fb000000100044006f0063006b002000430061006d0100000143000001090000000000000000fb0000000e004e00610076002000430061006d010000019d000000af0000000000000000000000010000016a00000455fc0200000004fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0049006d00610067006500000000330000029b0000000000000000fb0000000a00560069006500770073000000002800000396000000ad00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b20000000000000000000000020000078000000115fc0100000001fb0000000a00560069006500770073030000004e00000080000002e1000001970000000300000780000000fffc0100000002fc00000000000004380000000000fffffffa000000000200000002fb0000001e004d006f00740069006f006e00200050006c0061006e006e0069006e00670100000000ffffffff0000000000000000fb0000000800540069006d00650000000000ffffffff0000003b00fffffffb0000000800540069006d00650100000000000004500000000000000000000002c90000032e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 Selection: collapsed: false Time: @@ -706,5 +791,5 @@ Window Geometry: Views: collapsed: false Width: 1081 - X: 0 - Y: 0 + X: 49 + Y: 24 diff --git a/isaac/scripts/env_wrapper.sh b/isaac/scripts/env_wrapper.sh index 5bf94d93..9ccf119f 100755 --- a/isaac/scripts/env_wrapper.sh +++ b/isaac/scripts/env_wrapper.sh @@ -18,7 +18,17 @@ # License for the specific language governing permissions and limitations # under the License. -export ROS_DISTRO=kinetic +ubuntu_version=$(cat /etc/os-release | grep -oP "(?<=VERSION_CODENAME=).*") +if [ "$ubuntu_version" = "xenial" ]; then + export ROS_DISTRO=kinetic + export PYTHON_VERSION=2.7 +elif [ "$ubuntu_version" = "focal" ]; then + export ROS_DISTRO=noetic + export PYTHON_VERSION=3 +else + echo "Unsupported OS release. Found: $ubuntu_version" + exit 1 +fi # this script sets the ROS_IP, without it the other processors send back their hostname which can't be resolved if [ -e /etc/ros.sh ]; then @@ -47,7 +57,7 @@ export ROS_PACKAGE_PATH=${ff_root}/share:${ff_root}/stacks:${isaac_root}/share:$ export LD_LIBRARY_PATH=${ff_root}/lib:${isaac_root}/lib:${dds_root}:${ros_root}/lib:${ros_root}/lib/arm-linux-gnueabihf:/usr/local/lib:/lib:/usr/local/lib:${ff_root}/bin:${isaac_root}/bin export PATH=${ff_root}/bin:${isaac_root}/bin:${ros_root}/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin -export PYTHONPATH=${ff_root}/lib/python2.7/dist-packages:${isaac_root}/lib/python2.7/dist-packages:${ros_root}/lib/python2.7/dist-packages +export PYTHONPATH=${ff_root}/lib/python${PYTHON_VERSION}/dist-packages:${isaac_root}/lib/python${PYTHON_VERSION}/dist-packages:${ros_root}/lib/python${PYTHON_VERSION}/dist-packages export CMAKE_PREFIX_PATH=${ff_root}:${isaac_root}:${ros_root} unset dds_root diff --git a/isaac_msgs b/isaac_msgs deleted file mode 160000 index c5b40c62..00000000 --- a/isaac_msgs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c5b40c6248ce52b2bcc0ed1033492bab72a3fcb1 diff --git a/pano/README.md b/pano/README.md new file mode 100644 index 00000000..8c6e1aa6 --- /dev/null +++ b/pano/README.md @@ -0,0 +1,151 @@ +\page Panorama stitcher + +## Overview + +This document describes how to stitch panoramas collected by an Astrobee robot following an ISAAC panorama-style survey. + +We follow an approach using a Docker container that should be highly repeatable. Much of this process can also be run natively in your host OS if needed, but we don't cover that. + +## Install the Docker image for panorama stitching + +### Prerequisites + +- Make sure the Docker server is installed in your host operating system, following step 1 of the Docker option in the [`astrobee` repo installation instructions](https://github.com/nasa/astrobee/blob/develop/INSTALL.md). + +- Check out the source code from the `isaac` repo and set the `ISAAC_WS` environment variable following the [Instructions on installing and using the ISAAC Software](https://nasa.github.io/isaac/html/md_INSTALL.html) + +### Build the Docker image + +Run: +```bash +$ISAAC_WS/src/pano/docker/build.sh +``` + +## Stitch a batch of panoramas + +### Input requirements + +The expected input data for stitching a single panorama is: +- `bag_path`: Points to an Astrobee telemetry bag file recorded during an ISAAC panorama-style survey. The only SciCam image timestamps in the bag should be from within a single panorama. +- `images_dir`: Points to a folder containing the full-resolution SciCam image JPEG files saved on the HLP at the same time when the bag was recorded. Note that it's typical and no problem if a single image folder contains SciCam images that span multiple panoramas (and multiple bag files). However, the stitching script does assume all SciCam images for any given bag are in the same folder. + +Multiple panoramas can be stitched in a single batch job. + +### Pre-process the inputs + +Here are some typical pre-processing steps you might need before stitching when working with real Astrobee data: + +- The documentation on [Using Astrobee Robot Telemetry Logs](https://nasa.github.io/astrobee/html/using_telemetry.html) applies. For example, if processing older bags that have obsolete message types, you might need to run the `rosbag_fix_all.py` script. +- If you have a bag that includes telemetry from multiple panoramas (or other SciCam image timestamps), you should split it up so that each bag contains one panorama and no other SciCam data. +- You may find it more convenient to work with filtered telemetry bags that contains only the messages required by the panorama stitching. This is particularly useful if you need to transfer the bags to a different host before stitching (minimizing transfer data volume and storage required on the stitching host). It also speeds up stitching slightly. You can generate filtered bags like this: + ```bash + rosrun bag_processing scripts/rosbag_topic_filter.py -a /hw/cam_sci/compressed -a /loc/pose in1.bag in2.bag + # generates: filtered_in1.bag filtered_in2.bag + ``` + +### Configure folders for processing + +Run: +```bash +# choose your own folders in the host OS +export ISAAC_PANO_INPUT="$HOME/pano_input" +export ISAAC_PANO_OUTPUT="$HOME/pano_output" +``` + +The input folder will be mounted read-only in the Docker container at the mount point `/input`. It must contain the SciCam images and input bags for the panoramas (and no other bags). The panoramas will be detected with a recursive search, so the folder structure underneath the input folder shouldn't matter. + +The output folder will be mounted read-write in the Docker container at the mount point `/output`. It should typically be empty at the start of the stitching process. Stitched panoramas and other intermediate files will be written there. + +These environment variables will be used by the `run.sh` script in the next section. + +Because the folders are located in the host OS, stitched panorama outputs will persist even if the Docker container is stopped. + +### Start the container + +In your host OS, start the stitching container with the command below: +```bash +$ISAAC_WS/src/pano/docker/run.sh +``` + +This command should open an interactive terminal session running inside the container. If you exit the `run.sh` terminal session, the container will be stopped. Commands typed into the `run.sh` terminal will be run inside the container. + +You may also find it convenient to open additional terminals inside the container, which you can do as follows: +```bash +$ISAAC_WS/src/pano/docker/exec.sh bash +``` +You can exit terminal sessions started this way without stopping the container. + +In the sections below, commands to run inside the container can be typed into one of these `run.sh` or `exec.sh` terminal sessions inside the container, or you can run them directly from the host like this: +```bash +$ISAAC_WS/src/pano/docker/exec.sh mycmd arg1 arg2 ... +``` + +### Detect and configure the panoramas + +Inside the container, run: +```bash +rosrun pano_stitch scripts/config_panos.py +``` + +This will create the panorama config file `pano_meta.yaml` in the output folder. You should verify the config file looks correct by opening `$ISAAC_PANO_OUTPUT/pano_meta.yaml` in your favorite editor in the host OS. + +Below is an example `pano_meta.yaml`: +```yaml +scenes: + scene000_isaac10_queen_nod2_bay2: + bag_path: /input/isaac10_queen/20220617_1554_survey_nod2_bay2_std_panorama.bag + images_dir: /input/isaac10_queen/isaac_sci_cam_image_delayed + robot: queen + activity: isaac10 + module: nod2 + bay: 2 + position: + x: 10.996580718096382 + y: 0.0018100984828177873 + z: 4.899023194998069 + start_time: '2022-06-17T15:57:23.139000Z' + end_time: '2022-06-17T16:08:06.880000Z' + extra_stitch_args: '' + extra_tour_params: {} + scene001_isaac11_bumble_usl_bay6: + bag_path: /input/isaac11_bumble/20220711_1238_survey_usl_bay6_std_panorama_run1.bag + images_dir: /input/isaac11_bumble/isaac_sci_cam_image_delayed + robot: bumble + activity: isaac11 + module: usl + bay: 6 + position: + x: -0.3593667375653261 + y: 0.0072030887961762385 + z: 4.885617819225414 + start_time: '2022-07-11T12:40:00.841000Z' + end_time: '2022-07-11T12:51:01.391000Z' + extra_stitch_args: '' + extra_tour_params: {} +``` + +Ideally, the `config_panos.py` script will set all the config fields correctly, but it is not especially smart and could get fooled in some situations. It fills many of the later fields by attempting to parse the `bag_path`. If you want its auto-configure to work better, you can rename your bags in advance to filenames that follow the conventions in the example. If a field is not detected, its value will be set to `null`. + +Here's what to check in the config file: +- Each entry in the `scenes` list defines a single panorama to stitch. All of your panoramas should appear in the list. +- The header line for each panorama defines its `scene_id`. This id is normally not important, but it does control the name of the output subfolder for that panorama, as well as its scene id in the Pannellum tour. If you find a meaningful id helpful for debugging, you can set it to whatever you like, as long as every panorama has a unique id. +- The `bag_path` and `images_dir` fields should contain valid paths that specify the location of the input data for that panorama. They should match, in that all of the SciCam image timestamps in the bag should refer to SciCam images in the folder. +- The `robot` field should correctly specify the robot that collected the panorama. This field is used to look up the correct SciCam lens calibration parameters to use during stitching. +- The `activity`, `module`, `bay`, `position`, `start_time`, and `end_time` fields don't affect the stitching step but should be set correctly so they can be displayed to users in the final output tour. +- The `extra_stitch_args` field provides a way for advanced users to pass extra options to the `stitch_panorama.py` script for a specific panorama. It would typically be used for debugging and tuning when the stitching process fails or produces a low-quality result. +- The `extra_tour_params` field provides a way for advanced users to overwrite the parameters for a specific panorama in the `tour.json` file that configures the Pannellum display interface. + +### Stitch the panoramas and generate the tour + +Inside the container, run: +```bash +snakemake -s /src/isaac/src/pano/pano_view/scripts/Snakefile -d /output -c1 +``` + +This will trigger the `snakemake` build system to stitch the panoramas. There is one job per panorama in the config file, and `snakemake` will try to run these jobs in parallel up to the number of cores specified in the `-c` argument. (When `-c` is specified with no arguments, it will use the number of cores allocated to the container.) + +Note that some of the individual steps within each panorama stitch (e.g., `enblend`) run multi-threaded, and each individually tries to use all available cores, which could cause problems when stitching multiple panoramas in parallel. Both `snakemake` and `enblend` provide ways to manage this, which could be an area for future work. + +### View the tour + +TODO \ No newline at end of file diff --git a/pano/docker/build.sh b/pano/docker/build.sh new file mode 100755 index 00000000..29c19c11 --- /dev/null +++ b/pano/docker/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +SCRIPT_DIR=$(dirname "$0") +ISAAC_SRC=$(realpath "${SCRIPT_DIR}/../../") + +set -x +cd "${ISAAC_SRC}" +docker build . -f pano/docker/pano.Dockerfile -t isaac/pano diff --git a/pano/docker/exec.sh b/pano/docker/exec.sh new file mode 100755 index 00000000..3614ee2e --- /dev/null +++ b/pano/docker/exec.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -x +docker exec -it isaac_pano /bin/bash -ic "$*" diff --git a/pano/docker/pano.Dockerfile b/pano/docker/pano.Dockerfile new file mode 100644 index 00000000..49f1c28b --- /dev/null +++ b/pano/docker/pano.Dockerfile @@ -0,0 +1,72 @@ + +ARG UBUNTU_VERSION=20.04 +ARG REMOTE=ghcr.io/nasa + +FROM ${REMOTE}/isaac:latest-ubuntu${UBUNTU_VERSION} + +# default-jre: Java runtime needed for minifying Pannellum web files +# hugin: pano stitching tools (and hsi Python interface) +# libvips-tools: convert images to multires, zoomable in OpenSeaDragon +# python3-pip: for installing Python packages later in this Dockerfile +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + default-jre \ + hugin \ + libvips-tools \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +# pandas: pulled in as pyshtools dependency but install breaks if not mentioned explicitly (?) +# pyshtools: used during Pannellum multires generation +# snakemake: modern build system based on Python, manages stitching workflows +RUN pip3 install --no-cache-dir --upgrade pip \ + && pip3 install --no-cache-dir \ + pandas \ + pyshtools \ + snakemake + +# pannellum: library for viewing/navigating panorama tours +RUN mkdir -p /opt \ + && cd /opt \ + && git clone --quiet --depth 1 --branch standalone_load_event --single-branch --no-tags https://github.com/trey0/pannellum.git \ + && cd /opt/pannellum/utils/build \ + && python3 build.py + +# openseadragon: library for viewing high-res SciCam source images efficiently with zoom +RUN cd /tmp \ + && wget --quiet https://github.com/openseadragon/openseadragon/releases/download/v4.0.0/openseadragon-bin-4.0.0.tar.gz \ + && tar xfz openseadragon-bin-4.0.0.tar.gz \ + && rm openseadragon-bin-4.0.0.tar.gz \ + && mv openseadragon-bin-4.0.0 /opt/openseadragon + +# annotorious-openseadragon: image annotation plugin for OpenSeaDragon +RUN cd /tmp \ + && wget --quiet https://github.com/recogito/annotorious-openseadragon/releases/download/v2.7.10/annotorious-openseadragon-2.7.10.zip \ + && mkdir -p /tmp/anno \ + && cd /tmp/anno \ + && unzip -q ../annotorious-openseadragon-2.7.10.zip \ + && cd /tmp \ + && rm annotorious-openseadragon-2.7.10.zip \ + && mv anno /opt/annotorious-openseadragon + +# annotorious-selectorpack: additional geometry types for annotorious-seadragon +RUN cd /tmp \ + && wget --quiet https://github.com/recogito/annotorious-selector-pack/releases/download/v0.5.1/annotorious-selectorpack-0.5.1.zip \ + && mkdir -p /tmp/anno \ + && cd /tmp/anno \ + && unzip -q ../annotorious-selectorpack-0.5.1.zip \ + && cd /tmp \ + && rm annotorious-selectorpack-0.5.1.zip \ + && mv anno /opt/annotorious-selectorpack + +# annotorious-toolbar: toolbar for adding annotorious annotations +RUN mkdir -p /opt/annotorious-toolbar \ + && cd /opt/annotorious-toolbar \ + && wget --quiet https://cdn.jsdelivr.net/npm/@recogito/annotorious-toolbar@1.1.0/dist/annotorious-toolbar.min.js + +RUN echo 'source "/src/isaac/devel/setup.bash"\nexport ASTROBEE_CONFIG_DIR="/src/astrobee/src/astrobee/config"' >> "${HOME}/.bashrc" + +# Enables rosrun for pano packages. Can likely take this out +# once new pano folder is merged into develop and official docker +# images are updated. +RUN echo 'export ROS_PACKAGE_PATH="${ROS_PACKAGE_PATH}:/src/isaac/src/pano/pano_stitch::/src/isaac/src/pano/pano_view"' >> "${HOME}/.bashrc" diff --git a/pano/docker/run.sh b/pano/docker/run.sh new file mode 100755 index 00000000..f6513fb2 --- /dev/null +++ b/pano/docker/run.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +SCRIPT_DIR=$(dirname "$0") +ISAAC_SRC=$(realpath "${SCRIPT_DIR}/../../") + +if [ "$ISAAC_PANO_INPUT" = "" ] || [ ! -d "$ISAAC_PANO_INPUT" ]; then + echo "ISAAC_PANO_INPUT env var must point to input folder" + exit 1 +fi +if [ "$ISAAC_PANO_OUTPUT" = "" ] || [ ! -d "$ISAAC_PANO_OUTPUT" ]; then + echo "ISAAC_PANO_OUTPUT env var must point to output folder" + exit 1 +fi + +set -x +cd "${ISAAC_SRC}" +docker run \ + -it --rm \ + --name isaac_pano \ + --mount type=bind,source=${ISAAC_PANO_INPUT},target=/input,readonly \ + --mount type=bind,source=${ISAAC_PANO_OUTPUT},target=/output \ + --mount type=bind,source=${ISAAC_SRC},target=/src/isaac/src \ + isaac/pano diff --git a/scripts/docker/build_msgs_jar.Dockerfile b/pano/pano_stitch/CMakeLists.txt similarity index 55% rename from scripts/docker/build_msgs_jar.Dockerfile rename to pano/pano_stitch/CMakeLists.txt index ab93ca6b..b7c67942 100644 --- a/scripts/docker/build_msgs_jar.Dockerfile +++ b/pano/pano_stitch/CMakeLists.txt @@ -16,24 +16,13 @@ # License for the specific language governing permissions and limitations # under the License. -# 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 +# Check to see if correct version of media has been downloaded into tree. We +# dont do this if we are cross-compiling, as there is no need for it. We must +# also make sure the media is copied in a native install to a simulator. -ARG REMOTE=isaac -FROM ${REMOTE}/isaac:msgs-ubuntu16.04 +cmake_minimum_required(VERSION 2.8.3) +project(pano_stitch) -RUN apt-get update && apt-get install -y \ - unzip \ - libc6-dev-i386 \ - lib32z1 \ - python-wstool \ - openjdk-8-jdk \ - ros-kinetic-rosjava \ - && rm -rf /var/lib/apt/lists/* +find_package(catkin REQUIRED COMPONENTS) -# Compile msg jar files, genjava_message_artifacts only works with bash -RUN ["/bin/bash", "-c", "cd /src/msgs \ - && catkin config \ - && catkin build \ - && . devel/setup.bash \ - && genjava_message_artifacts --verbose -p ff_msgs ff_hw_msgs isaac_msgs isaac_hw_msgs"] +catkin_package() diff --git a/pano/pano_stitch/config/README.md b/pano/pano_stitch/config/README.md new file mode 100644 index 00000000..a2d36e2e --- /dev/null +++ b/pano/pano_stitch/config/README.md @@ -0,0 +1,2 @@ + +- `isaac_phase1x_panos.csv`: Summary of ISAAC Phase 1X panorama data selected to include in the first panoramic tour. The `/testsessions` path used here can be found at `hivemind.ndc.nasa.gov:/home/p-astrobee/webdir/testsessions` (or corresponding web URLs). Used by `scripts/collect_pano_inputs.py`. \ No newline at end of file diff --git a/pano/pano_stitch/config/isaac_phase1x_panos.csv b/pano/pano_stitch/config/isaac_phase1x_panos.csv new file mode 100644 index 00000000..35cafdb3 --- /dev/null +++ b/pano/pano_stitch/config/isaac_phase1x_panos.csv @@ -0,0 +1,11 @@ +activity,robot,bag_path,images_dir_rel_bag_path +isaac9,queen,/testsessions/2022-06-08_115/robot_data/SN005/bags/20220608_1401_survey_jem_bay7_std_panorama.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac6,queen,/testsessions/2022-04-07_109/robot_data/SN005/bags/20220407_1601_survey_bay6_panorama.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac9,queen,/testsessions/2022-06-08_115/robot_data/SN005/bags/20220608_1336_survey_jem_bay5_std_panorama.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac9,queen,/testsessions/2022-06-08_115/robot_data/SN005/bags/20220608_1309_survey_jem_bay4_std_panorama.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac9,bumble,/testsessions/2022-06-08_115/robot_data/SN003/bags/20220608_1305_survey_jem_bay2_std_panorama.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac10,queen,/testsessions/2022-06-17_118/robot_data/SN005/bags/20220617_1554_survey_nod2_bay2_std_panorama.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac11,queen,/testsessions/2022-07-11_122/robot_data/SN005/bags/20220711_1433_survey_usl_bay1_std_panorama_run1.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac11,bumble,/testsessions/2022-07-11_122/robot_data/SN003/bags/20220711_1426_survey_usl_bay4_std_panorama_run_1.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac11,bumble,/testsessions/2022-07-11_122/robot_data/SN003/bags/20220711_1255_survey_usl_bay5_std_panorama_run_1.bag,../user_data/isaac_sci_cam_image_delayed/ +isaac11,bumble,/testsessions/2022-07-11_122/robot_data/SN003/bags/20220711_1238_survey_usl_bay6_std_panorama_run1.bag,../user_data/isaac_sci_cam_image_delayed/ diff --git a/pano/pano_stitch/package.xml b/pano/pano_stitch/package.xml new file mode 100644 index 00000000..f4ea4030 --- /dev/null +++ b/pano/pano_stitch/package.xml @@ -0,0 +1,19 @@ + + + pano_stitch + 0.0.0 + The pano_stitch package + + Apache License, Version 2.0 + + + ISAAC Flight Software + + + ISAAC Flight Software + + + catkin + rosbag + rosbag + diff --git a/pano/pano_stitch/scripts/collect_pano_inputs.py b/pano/pano_stitch/scripts/collect_pano_inputs.py new file mode 100755 index 00000000..181904f9 --- /dev/null +++ b/pano/pano_stitch/scripts/collect_pano_inputs.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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 script for collecting, fixing, and filtering pano input data +prior to transferring it to another host for stitching. The relevant +bags and images are specifed in a CSV config file. As the bags are +collected, some CSV metadata will be embedded in the folder names so +config_panos.py should be able to detect it. + +This script is designed to be run in a Vagrant box on the hivemind +server where "hivemind:/home/p-astrobee/webdir/testsessions" is +mounted in the Vagrant box at "/testsessions". +""" + +import argparse +import csv +import glob +import os +import sys + +import pano_image_meta + +PANO_STITCH_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +DEFAULT_CONFIG = os.path.join(PANO_STITCH_ROOT, "config", "isaac_phase1x_panos.csv") +FILTER_ARGS = "-a /hw/cam_sci/compressed -a /loc/pose" + + +def dosys(cmd, exit_on_error=True, echo=True): + if echo: + print("+ " + cmd) + ret = os.system(cmd) + if ret != 0: + msg = "Command exited with non-zero return value %s" % ret + if exit_on_error: + raise RuntimeError(msg) + print(msg) + return ret + + +def get_image_path(images_dir, img_name): + img_path = os.path.join(images_dir, img_name) + if os.path.exists(img_path): + return img_path + + # special case fallback for ISAAC6 pano with weird sci_cam_image filenames + sec, subsec, ext = img_name.split(".") + img_glob = os.path.join(images_dir, "%s.*.%s" % (sec, ext)) + candidates = glob.glob(img_glob) + if len(candidates) == 0: + return img_path + + use_path = candidates[0] + if len(candidates) > 1: + print("warning: multiple images match %s" % img_glob) + print(" arbitrarily using first match %s" % use_path) + return use_path + + +def collect_pano_inputs(config_path, out_path, num_jobs): + # add provenance info + if not os.path.exists(out_path): + dosys("mkdir -p %s" % out_path) + dosys("cp %s %s" % (config_path, out_path)) + readme_path = os.path.join(out_path, "README.txt") + with open(readme_path, "w") as readme: + readme.write("Panorama inputs collected by collect_pano_inputs.py\n") + readme.write("Command was: %s\n" % sys.argv) + readme.write("See config file: %s" % os.path.basename(config_path)) + + with open(config_path, "r") as config_stream: + panos = list(csv.DictReader(config_stream)) + + for pano in panos: + pano["sub_out"] = "%s/%s_%s" % (out_path, pano["activity"], pano["robot"]) + pano["out_bag"] = os.path.join( + pano["sub_out"], os.path.basename(pano["bag_path"]) + ) + pano["out_bag_partial"] = os.path.join( + pano["sub_out"], "partial-" + os.path.basename(pano["bag_path"]) + ) + pano["out_bag_fix_all"] = ( + os.path.splitext(pano["out_bag_partial"])[0] + ".fix_all.bag" + ) + + if os.path.exists(pano["out_bag"]): + print("filtered bag %s exists, not overwriting" % pano["out_bag"]) + continue + + if not os.path.isdir(pano["sub_out"]): + dosys("mkdir -p %s" % pano["sub_out"]) + + # First filtering directly with rosbag_topic_filter.py. This should work + # if the bag was already fixed. + for pano in panos: + if os.path.exists(pano["out_bag"]): + continue + ret = dosys( + "rosrun bag_processing rosbag_topic_filter.py %s %s -o %s" + % (pano["bag_path"], FILTER_ARGS, pano["out_bag_partial"]), + exit_on_error=False, + ) + if ret == 0: + dosys("mv %s %s" % (pano["out_bag_partial"], pano["out_bag"])) + else: + print( + "%s: simple bag filtering failed - will try to fix and filter this bag later" + % pano["bag_path"] + ) + print() + + ###################################################################### + # Fix and filter (only applied if simple filter didn't work) + + # Fix and filter step 1 - symlink + for pano in panos: + if os.path.exists(pano["out_bag"]): + continue + # Make a (symlink) temporary copy of the input bag in the + # desired output folder because we can't otherwise control the + # rosbag_fix_all.py output path. Use a symlink to avoid + # unnecessarily copying the large bag file. + dosys("ln -sf %s %s" % (pano["bag_path"], pano["out_bag_partial"])) + + # Fix and filter step 2 - rosbag_fix_all.py (runs in parallel) + bags_to_filter = [ + pano["out_bag_partial"] for pano in panos if not os.path.exists(pano["out_bag"]) + ] + if bags_to_filter: + dosys( + "rosrun bag_processing rosbag_fix_all.py --filter='%s' -j%s --no-merge %s" + % (FILTER_ARGS, num_jobs, " ".join(bags_to_filter)) + ) + + # Fix and filter step 3 - Delete temp symlinks and rename fixed bags + for pano in panos: + if os.path.exists(pano["out_bag"]): + continue + + dosys("rm %s" % pano["out_bag_partial"]) + dosys("mv %s %s" % (pano["out_bag_fix_all"], pano["out_bag"])) + + # Copy images referenced by bags + for pano in panos: + images_dir = os.path.realpath( + os.path.join( + os.path.dirname(pano["bag_path"]), pano["images_dir_rel_bag_path"] + ) + ) + + out_images_dir = os.path.join(pano["sub_out"], os.path.basename(images_dir)) + if not os.path.isdir(out_images_dir): + dosys("mkdir -p %s" % out_images_dir) + + images = pano_image_meta.get_image_meta(pano["out_bag"]) + copied = 0 + missing = 0 + for img in images: + ret = dosys( + "cp -n %s %s" + % ( + get_image_path(images_dir, img["img_path"]), + os.path.join(out_images_dir, img["img_path"]), + ), + echo=False, + exit_on_error=False, + ) + if ret == 0: + copied += 1 + else: + missing += 1 + print( + "Images for bag %s: %s copied, %s missing" + % (pano["bag_path"], copied, missing) + ) + + # Collection complete. Tell the user how to sync to the remote host. + print() + print( + "Would run the following transfer command, but due to security rules you may\n" + "need to initiate the rsync from the other side:" + ) + print(" rsync -rz --info=progress2 %s " % out_path) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "-c", + "--config", + type=str, + help="input path for CSV listing of pano inputs", + default=DEFAULT_CONFIG, + required=False, + ) + parser.add_argument( + "-o", + "--output", + type=str, + help="where to output collected pano stitching inputs", + default="/shared/collect_pano_inputs_{}".format(os.environ["USER"]), + required=False, + ) + parser.add_argument( + "-j", + "--jobs", + type=int, + help="number of jobs to run in parallel (passed to rosbag_fix_all.py)", + default=4, + required=False, + ) + + args = parser.parse_args() + + collect_pano_inputs(args.config, args.output, args.jobs) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_stitch/scripts/compare_pto.py b/pano/pano_stitch/scripts/compare_pto.py new file mode 100755 index 00000000..40dcae67 --- /dev/null +++ b/pano/pano_stitch/scripts/compare_pto.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Compare PTO files. +""" + +import argparse +import array +import os + +import hsi +import matplotlib +import numpy as np + +matplotlib.use("Agg") # must call before importing pyplot +from matplotlib import pyplot as plt + + +def read_pto(pano, pto_path): + ifs = hsi.ifstream(pto_path) + pano.readData(ifs) + + +def get_params(pto_path, x_param, y_param, lonlat): + pano = hsi.Panorama() + read_pto(pano, pto_path) + + result_in = array.array("d") + for i in range(pano.getNrOfImages()): + img = pano.getImage(i) + result_in.extend((img.getVar(x_param), img.getVar(y_param))) + + result = np.array(result_in, dtype=np.float64).reshape((-1, 2)) + if lonlat: + result = normalize_lonlat(result) + return result + + +def xyz_from_lonlat(lonlat): + lon = lonlat[:, 0] * np.pi / 180 + lat = lonlat[:, 1] * np.pi / 180 + result = np.array( + (np.cos(lat) * np.cos(lon), np.cos(lat) * np.sin(lon), np.sin(lat)) + ).T + return result + + +def lonlat_from_xyz(xyz): + x = xyz[:, 0] + y = xyz[:, 1] + z = xyz[:, 2] + lon = np.arctan2(y, x) + lat = np.arctan2(z, np.sqrt(x * x + y * y)) + return np.array((lon * 180 / np.pi, lat * 180 / np.pi)).T + + +def normalize_lonlat(lonlat): + return lonlat_from_xyz(xyz_from_lonlat(lonlat)) + + +def lon_wrap_line(x_line, y_line): + x1, x2 = x_line + y1, y2 = y_line + if abs(x1 - x2) > 180: + if x1 < x2: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + x2p = x2 + 360 + yb = y1 + (y2 - y1) * (180 - x1) / (x2p - x1) + return (x1, 180, -180, x2), (y1, yb, yb, y2) + else: + return x_line, y_line + + +def lon_wrap_lines(x, y): + xa = array.array("d") + ya = array.array("d") + for x_line, y_line in zip(x.T, y.T): + x_lines, y_lines = lon_wrap_line(x_line, y_line) + xa.extend(x_lines) + ya.extend(y_lines) + x_out = np.array(xa, dtype=np.float64).reshape((-1, 2)).T + y_out = np.array(ya, dtype=np.float64).reshape((-1, 2)).T + return x_out, y_out + + +def plot_params(ptos, x_param, y_param, out_path, lonlat=False): + fig, ax = plt.subplots() + prev_data = None + + for pto in ptos: + data = get_params(pto, x_param, y_param, lonlat) + plt.plot(data[:, 0], data[:, 1], "+") + if prev_data is not None: + x = np.array([prev_data[:, 0], data[:, 0]]) + y = np.array([prev_data[:, 1], data[:, 1]]) + if lonlat: + x, y = lon_wrap_lines(x, y) + plt.plot(x, y, "-", color="gray") + prev_data = data + + pos = ax.get_position() + ax.set_position([pos.x0, pos.y0, pos.width * 0.7, pos.height]) + pto_names = [os.path.splitext(os.path.basename(pto))[0] for pto in ptos] + ax.legend(pto_names, loc="upper left", bbox_to_anchor=(1.05, 1)) + plt.xlabel(x_param) + plt.ylabel(y_param) + + if lonlat: + tick_size = 30 + ax.set_xticks(range(-180, 180 + tick_size, tick_size)) + ax.set_yticks(range(-90, 90 + tick_size, tick_size)) + ax.axis([-180, 180, -90, 90]) + + plt.grid() + ax.axis("scaled") + + # plt.tight_layout() + fig.set_size_inches((10, 5)) + + plt.savefig(out_path) + print("Wrote plot to %s" % out_path) + plt.close() + + +def compare_pto(pto_paths): + plot_params(pto_paths, "y", "p", "compare_pto_y_p.png", lonlat=True) + plot_params(pto_paths, "Tpy", "Tpp", "compare_pto_Tpy_Tpp.png", lonlat=True) + plot_params(pto_paths, "y", "r", "compare_pto_y_r.png") + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "pto", + nargs="+", + help="input pto path", + ) + args = parser.parse_args() + + compare_pto(args.pto) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_stitch/scripts/config_panos.py b/pano/pano_stitch/scripts/config_panos.py new file mode 100755 index 00000000..ea17f098 --- /dev/null +++ b/pano/pano_stitch/scripts/config_panos.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Detect panoramas (bag files and associated SciCam images) and write +config file for stitching. Manual review of the config file is +recommended before starting a large stitching job. + +If --add is specified, add only newly detected panoramas to the +existing config file, without modifying the existing entries. +""" + +import argparse +import copy +import datetime +import os +import random +import re +import sys + +import numpy as np +import yaml + +import pano_image_meta + +SCI_CAM_IMG_REGEX = re.compile(r"\d{10}\.\d{3}\.jpg$") + +ROBOT_REGEX = re.compile(r"(\b|_)(?Phoney|bumble|queen)(\b|_)", re.IGNORECASE) +ACTIVITY_REGEX = re.compile(r"(\b|_)(?Pisaac\d+)(\b|_)", re.IGNORECASE) +MODULE_REGEX = re.compile( + r"(\b|_)(?PJEM|NOD2|USL|COL|NOD1)(\b|_)", re.IGNORECASE +) +BAY_REGEX = re.compile(r"(\b|_)bay(?P\d+)(\b|_)", re.IGNORECASE) + +SCENE_REGEXES = ( + ("activity", ACTIVITY_REGEX), + ("robot", ROBOT_REGEX), + ("module", MODULE_REGEX), + ("bay", BAY_REGEX), +) + +FIELD_PREFIXES = { + "bay": "bay", +} + + +def get_scene_position(bag_meta): + pos_data = np.zeros((len(bag_meta), 3)) + for i, image_meta in enumerate(bag_meta): + pos_data[i, :] = [image_meta["x"], image_meta["y"], image_meta["z"]] + median_pos = np.median(pos_data, axis=0) + return { + "x": float(median_pos[0]), + "y": float(median_pos[1]), + "z": float(median_pos[2]), + } + + +def get_image_timestamp(image_meta): + timestamp = datetime.datetime.utcfromtimestamp(image_meta["timestamp"]) + return timestamp.isoformat() + "Z" + + +def detect_pano_meta(in_folder): + """ + Detect panoramas (bag files and associated SciCam images). Return + pano metadata. + """ + + bags = {} + sci_cam_images = {} + for dirname, subdirs, files in os.walk(in_folder): + for f in files: + if f.endswith(".bag"): + bag_path = os.path.join(dirname, f) + bags[bag_path] = pano_image_meta.get_image_meta(bag_path) + elif SCI_CAM_IMG_REGEX.search(f): + sci_cam_images[f] = dirname + print("Detected {} candidate bags".format(len(bags))) + print("Detected {} candidate SciCam images".format(len(sci_cam_images))) + + scenes = {} + pano_meta = {"scenes": scenes} + for i, (bag_path, bag_meta) in enumerate(bags.items()): + print("Bag {}".format(bag_path)) + print(" {} SciCam images with pose data".format(len(bag_meta))) + if not bag_meta: + print(" (Skipping)") + continue + + scene_meta = { + "bag_path": bag_path, + "images_dir": None, + "robot": None, + "activity": None, + "module": None, + "bay": None, + "position": get_scene_position(bag_meta), + "start_time": get_image_timestamp(bag_meta[0]), + "end_time": get_image_timestamp(bag_meta[-1]), + "extra_stitch_args": "", + "extra_tour_params": {}, + } + scene_meta["images_dir"] = sci_cam_images.get(bag_meta[0]["img_path"]) + + for field, regex in SCENE_REGEXES: + match = regex.search(bag_path) + if match: + val = match.group(field).lower() + if val.isdigit(): + val = int(val) + scene_meta[field] = val + + scene_id = "scene%03d" % i + for field, regex in SCENE_REGEXES: + if field in scene_meta: + scene_id += "_%s%s" % (FIELD_PREFIXES.get(field, ""), scene_meta[field]) + + scenes[scene_id] = scene_meta + + return pano_meta + + +def write_pano_meta(pano_meta, out_yaml_path): + """ + Write pano metadata to a YAML file, to be used as a configfile for + snakemake. + """ + with open(out_yaml_path, "w") as out_yaml: + out_yaml.write( + yaml.safe_dump(pano_meta, default_flow_style=False, sort_keys=False) + ) + print("wrote to %s" % out_yaml_path) + + +def add_pano_meta(new_meta, out_yaml_path): + """ + Add new_meta to the existing pano metadata in the YAML file. + """ + with open(out_yaml_path, "r") as old_yaml_stream: + old_meta = yaml.safe_load(old_yaml_stream) + old_bag_paths = set( + (os.path.realpath(scene["bag_path"]) for scene in old_meta["scenes"].values()) + ) + + merged_meta = copy.deepcopy(old_meta) + scene_prefix = re.compile(r"scene\d\d\d_") + for scene_id, scene_meta in new_meta["scenes"].items(): + new_bag_path = os.path.realpath(scene_meta["bag_path"]) + if new_bag_path in old_bag_paths: + continue + + # renumber scene id + scene_id = scene_prefix.sub("scene%03d_" % len(merged_meta["scenes"]), scene_id) + + merged_meta["scenes"][scene_id] = scene_meta + + num_old = len(old_meta["scenes"]) + num_new = len(new_meta["scenes"]) + num_out = len(merged_meta["scenes"]) + num_added = num_out - num_old + num_skipped = num_new - num_added + + print( + "out of %d panos detected, %d were added and %d existing entries were skipped" + % (num_new, num_added, num_skipped) + ) + + tmp_path = out_yaml_path + ".tmp" + with open(tmp_path, "w") as out_yaml: + out_yaml.write( + yaml.safe_dump(merged_meta, default_flow_style=False, sort_keys=False) + ) + + stem, suffix = os.path.splitext(out_yaml_path) + random.seed() + unique_id = "%0x" % random.getrandbits(32) + old_yaml_backup_path = "%s-old-%s%s" % (stem, unique_id, suffix) + + os.rename(out_yaml_path, old_yaml_backup_path) + os.rename(tmp_path, out_yaml_path) + + print("wrote to %s" % out_yaml_path) + print("old version backed up at %s" % old_yaml_backup_path) + + +def config_panos(in_folder, out_yaml_path, force, add_panos): + if os.path.exists(out_yaml_path) and not (force or add_panos): + print( + "output file %s exists, not overwriting (did you mean --force or --add?)" + % out_yaml_path + ) + sys.exit(1) + + pano_meta = detect_pano_meta(in_folder) + if os.path.exists(out_yaml_path) and add_panos: + add_pano_meta(pano_meta, out_yaml_path) + else: + write_pano_meta(pano_meta, out_yaml_path) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "-i", + "--in-folder", + type=str, + help="input path for folder to search for bag files and SciCam images", + default="/input", + required=False, + ) + parser.add_argument( + "-o", + "--out-yaml", + type=str, + help="output path for YAML pano stitch config", + default="/output/pano_meta.yaml", + required=False, + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + help="overwrite output file if it exists", + default=False, + required=False, + ) + parser.add_argument( + "-a", + "--add", + action="store_true", + help="add new panos to existing file without changing old ones", + default=False, + required=False, + ) + args = parser.parse_args() + + config_panos(args.in_folder, args.out_yaml, args.force, args.add) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_stitch/scripts/downsample_pto.py b/pano/pano_stitch/scripts/downsample_pto.py new file mode 100755 index 00000000..d444e3dc --- /dev/null +++ b/pano/pano_stitch/scripts/downsample_pto.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Downsample pano input frames, metadata, control points, and requested output +size in PTO. Convenient to speed up the rest of the stitching process when +debugging. + +Example: + rosrun inspection scripts/downsample_pto.py in.pto --factor 4 out.pto +""" + +import argparse +import os + +import cv2 +import hsi + + +def downsample_frame(in_path, out_path, factor): + print(" downsample_frame %s %s" % (in_path, out_path)) + + if os.path.exists(out_path): + print(" output file already exists, skipping") + return + + im = cv2.imread(in_path, cv2.IMREAD_UNCHANGED) + rows, cols = im.shape[:2] + down_size = (int(cols / factor), int(rows / factor)) + down_im = cv2.resize(im, down_size, interpolation=cv2.INTER_LINEAR) + cv2.imwrite(out_path, down_im) + + +def read_pto(pano, pto_path): + ifs = hsi.ifstream(pto_path) + pano.readData(ifs) + + +def write_pto(pano, pto_path): + ofs = hsi.ofstream(pto_path) + pano.writeData(ofs) + + +def downsampled_path(in_path): + in_prefix, in_ext = os.path.splitext(in_path) + return in_prefix + "_down" + in_ext + + +def downsample_pto(in_path, out_path, factor): + if os.path.exists(out_path): + print("Output file %s exists, not overwriting." % out_path) + return + + pano = hsi.Panorama() + read_pto(pano, in_path) + + # process input images + print("Downsampling:") + for i in range(pano.getNrOfImages()): + img = pano.getImage(i) + orig_path = img.getFilename() + down_path = downsampled_path(orig_path) + + # downsample image + downsample_frame(orig_path, down_path, factor) + + # update pto: point to dowsampled image + img.setFilename(down_path) + + # update pto: image width and height + img_size = img.getSize() + img_size.x = int(img_size.x / factor) + img_size.y = int(img_size.y / factor) + img.setSize(img_size) + + # process control points + pts = pano.getCtrlPoints() + for pt in pts: + pt.x1 /= factor + pt.x2 /= factor + pt.y1 /= factor + pt.y2 /= factor + pano.setCtrlPoints(pts) + + # reduce size of output pano + opts = pano.getOptions() + w, h = opts.getWidth(), opts.getHeight() + # Note: the setWidth() call sometimes auto-updates height, so it's important + # that we queried both w and h before setting either parameter. + opts.setWidth(int(w / factor)) + opts.setHeight(int(h / factor)) + + write_pto(pano, out_path) + print("Wrote to %s" % out_path) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "in_pto", + type=str, + help="input pto path", + ) + parser.add_argument( + "out_pto", + type=str, + help="output pto path", + ) + parser.add_argument( + "-f", + "--factor", + type=float, + default=8, + help="downsample by specified factor", + ) + args = parser.parse_args() + + downsample_pto(args.in_pto, args.out_pto, args.factor) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_stitch/scripts/opencv_convert.py b/pano/pano_stitch/scripts/opencv_convert.py new file mode 100755 index 00000000..913b3622 --- /dev/null +++ b/pano/pano_stitch/scripts/opencv_convert.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Convenience script for converting image formats (with optional resizing) using +OpenCV. You may find it useful for large panorama images because OpenCV has +larger image size limits than ImageMagick convert. + +Example: + rosrun inspection scripts/opencv_convert.py pano.png -resize 4000x4000 pano_small.jpg +""" + +import argparse + +import cv2 + + +def opencv_convert(in_path, out_path, resize): + im = cv2.imread(in_path, cv2.IMREAD_UNCHANGED) + + if resize: + # parse input string + w_str, h_str = resize.split("x") + w = int(w_str) + h = int(h_str) + + # preserve aspect ratio, interpreting requested dimensions as limits on + # output width and height, like ImageMagick convert. + in_h, in_w = im.shape[:2] + scale_factor = min(float(w) / in_w, float(h) / in_h) + resize_aspect = tuple([int(round(scale_factor * val)) for val in (in_w, in_h)]) + + # perform resize operation + im = cv2.resize(im, resize_aspect, interpolation=cv2.INTER_CUBIC) + + cv2.imwrite(out_path, im) + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument( + "in_image", + type=str, + help="input image path", + ) + parser.add_argument( + "out_image", + type=str, + help="output image path", + ) + parser.add_argument( + "-r", + "--resize", + type=str, + default=None, + help="limit output size to specified dimensions, like '640x480'. Input aspect ratio will be preserved within the specified limits.", + ) + args = parser.parse_args() + + opencv_convert(args.in_image, args.out_image, args.resize) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_stitch/scripts/pano_image_meta.py b/pano/pano_stitch/scripts/pano_image_meta.py new file mode 100755 index 00000000..cd971636 --- /dev/null +++ b/pano/pano_stitch/scripts/pano_image_meta.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Extract pano image poses and write to CSV for convenient debug plotting. +""" + +import argparse +import csv + +import rosbag +from tf.transformations import euler_from_quaternion + +IMAGE_TOPIC = "/hw/cam_sci/compressed" +POSE_TOPIC = "/loc/pose" +FIELD_NAMES = ( + "timestamp", + "img_path", + "x", + "y", + "z", + "roll", + "pitch", + "yaw", +) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def get_image_meta(inbag_path, num_images=None): + images = [] + with rosbag.Bag(inbag_path) as bag: + img_meta = None + for topic, msg, t in bag.read_messages([IMAGE_TOPIC, POSE_TOPIC]): + if topic == IMAGE_TOPIC: + if num_images is not None and len(images) == num_images: + break + + img_meta = {} + images.append(img_meta) + + # fill meta from image message + img_meta["timestamp"] = ( + msg.header.stamp.secs + 1e-9 * msg.header.stamp.nsecs + ) + img_meta["img_path"] = "%d.%03d.jpg" % ( + msg.header.stamp.secs, + msg.header.stamp.nsecs * 1e-6, + ) + + if img_meta is not None and topic == POSE_TOPIC: + # fill meta from the next pose message after the image message + img_meta["x"] = msg.pose.position.x + img_meta["y"] = msg.pose.position.y + img_meta["z"] = msg.pose.position.z + + orientation_list = [ + msg.pose.orientation.x, + msg.pose.orientation.y, + msg.pose.orientation.z, + msg.pose.orientation.w, + ] + (roll, pitch, yaw) = euler_from_quaternion(orientation_list) + img_meta["roll"] = roll + img_meta["pitch"] = pitch + img_meta["yaw"] = yaw + + img_meta = None + + return images + + +def pano_image_meta(in_bag, out_csv): + images = get_image_meta(in_bag) + with open(out_csv, "w") as out: + writer = csv.DictWriter(out, fieldnames=FIELD_NAMES) + writer.writeheader() + for img_meta in images: + writer.writerow(img_meta) + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "in_bag", + type=str, + help="Input bagfile containing SciCam images and pose messages.", + ) + parser.add_argument( + "out_csv", + type=str, + help="Output CSV with metadata", + ) + args = parser.parse_args() + + pano_image_meta(args.in_bag, args.out_csv) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_stitch/scripts/stitch_panorama.py b/pano/pano_stitch/scripts/stitch_panorama.py new file mode 100755 index 00000000..3c56a024 --- /dev/null +++ b/pano/pano_stitch/scripts/stitch_panorama.py @@ -0,0 +1,745 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Generate/update Hugin PTO files and stitch a panorama. + +Example: + export ASTROBEE_RESOURCE_DIR=$SOURCE_PATH/astrobee/resources + export ASTROBEE_CONFIG_DIR=$SOURCE_PATH/astrobee/config + export ASTROBEE_WORLD=granite + export ASTROBEE_ROBOT=bsharp + rosrun inspection scripts/stitch_panorama.py in.bag --images-dir=isaac_sci_cam_delayed +""" + +import argparse +import datetime +import math +import os +import shutil +import subprocess +import sys + +import cv2 +import hsi +import rosbag +from tf.transformations import euler_from_quaternion + +RAD2DEG = 180 / math.pi +IMAGE_TOPIC = "/hw/cam_sci_info" +POSE_TOPIC = "/loc/pose" +UNDISTORT_ENV_VARS = [ + "ASTROBEE_RESOURCE_DIR", + "ASTROBEE_CONFIG_DIR", + "ASTROBEE_WORLD", + "ASTROBEE_ROBOT", +] +# List of Hugin variables that relate to camera intrinsics. There are many +# more, but these are the only ones we optimize. +LENS_PARAMS = ("v", "b") +TRANSLATION_PARAMS = ("TrX", "TrY", "TrZ", "Tpy", "Tpp") + + +def quote_if_needed(arg): + if " " in arg: + return '"' + arg + '"' + else: + return arg + + +def run_cmd(cmd, path_prefix=None): + print("run_cmd: " + " ".join([quote_if_needed(arg) for arg in cmd])) + + if path_prefix: + env = os.environ.copy() + env["PATH"] = path_prefix + ":" + env["PATH"] + popen = subprocess.Popen(cmd, env=env) + else: + popen = subprocess.Popen(cmd) + return_code = popen.wait() + if return_code: + raise subprocess.CalledProcessError(return_code, cmd) + + return return_code + + +def catkin_find(pkg, subpath): + return ( + subprocess.check_output(["catkin_find", "--first-only", pkg, subpath]) + .decode("utf-8") + .strip() + ) + + +def fill_if_missing(var, value): + if os.environ.get(var) is None: + if callable(value): + value = value() + os.environ[var] = value + print( + "Environment variable was not specified, setting auto-detected / default value:" + ) + print(" Variable: %s" % var) + print(" Value: %s" % value) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def parse_args(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "inbag", + type=str, + help="Input bagfile containing SciCam config and pose messages.", + ) + parser.add_argument( + "--output-dir", + type=str, + required=False, + default="stitch_{inbag}", + help="Directory to write stitched pano and intermediate files", + ) + parser.add_argument( + "--images-dir", + type=str, + required=False, + default=".", + help="Directory to read SciCam images from.", + ) + parser.add_argument( + "--no-stitching", + dest="no_stitching", + action="store_true", + default=False, + help="Generate hugin and optimize only.", + ) + parser.add_argument( + "--stitching-only", + dest="only_stitching", + action="store_true", + default=False, + help="Stitch hugin file only", + ) + parser.add_argument( + "--no-undistort", + action="store_true", + default=False, + help="Don't undistort input images before stitching", + ) + parser.add_argument( + "--no-lens", + action="store_true", + default=False, + help="Disable optimization of lens parameters", + ) + parser.add_argument( + "--no-translation", + action="store_true", + default=False, + help="Disable optimization of translation parameters", + ) + parser.add_argument( + "--no-log", + action="store_true", + default=False, + help="Disable duplicating console output to log file", + ) + parser.add_argument( + "--skip-images", + type=str, + required=False, + default=None, + help="Skip specified images. Comma-separated list of substrings to match image file against (you can just specify the timestamps of the images you want to skip).", + ) + parser.add_argument( + "--enblend-options", + type=str, + required=False, + default="--primary-seam-generator=nft", + help="Extra options to pass to enblend. Should be a quoted string that can contain multiple options. Sometimes adding --no-optimize --fine-mask can help. See comments in script.", + ) + parser.add_argument( + "--world", + type=str, + required=False, + default=None, + help="Override ASTROBEE_WORLD environment variable", + ) + parser.add_argument( + "--robot", + type=str, + required=False, + default=None, + help="Override ASTROBEE_ROBOT environment variable", + ) + + args = parser.parse_args() + + # override environment variables if user specified command-line args + if args.world is not None: + os.environ["ASTROBEE_WORLD"] = args.world + if args.robot is not None: + os.environ["ASTROBEE_ROBOT"] = args.robot + + return args + + +def get_image_meta(inbag_path, images_dir, skip_images_str): + src_images = [] + if skip_images_str is None: + skip_images = [] + else: + skip_images = skip_images_str.split(",") + with rosbag.Bag(inbag_path) as bag: + need_pose = False + print("Detecting images:") + for topic, msg, t in bag.read_messages([IMAGE_TOPIC, POSE_TOPIC]): + if topic == IMAGE_TOPIC: + img_path = os.path.join( + images_dir, + str(msg.header.stamp.secs) + + "." + + "%03d" % (msg.header.stamp.nsecs * 0.000001) + + ".jpg", + ) + print(" " + img_path) + + img_base = os.path.basename(img_path) + skip_matches = [s for s in skip_images if s in img_base] + if skip_matches: + print( + " Matches one of --skip-images (%s), skipping" + % skip_matches[0] + ) + continue + + # Make sure image exists + if not os.path.exists(img_path): + raise RuntimeError("Could not find %s" % img_path) + + # Gather image metadata + src_image = hsi.SrcPanoImage() + src_image.setVar("v", 62) # about right, may override later + src_image.setFilename(os.path.realpath(img_path)) + src_images.append(src_image) + + need_pose = True + + if need_pose and topic == POSE_TOPIC: + # Configure hugin parameters + orientation_list = [ + msg.pose.orientation.x, + msg.pose.orientation.y, + msg.pose.orientation.z, + msg.pose.orientation.w, + ] + (roll, pitch, yaw) = euler_from_quaternion(orientation_list) + src_image.setVar("r", roll * RAD2DEG) + src_image.setVar("p", pitch * RAD2DEG) + src_image.setVar("y", yaw * RAD2DEG) + + # If y/p are very far from Tpy/Tpp (difference approaching 90 + # degrees or greater), the remapping math gets weird and nona + # can produce nonsensical output. And not sure how to + # interpret Tpy/Tpp. Like either or both could have opposite + # sign from y/p. It's clear there's a real problem here, but + # this change to try and help if anything made it worse so far. + # https://hugin.sourceforge.io/docs/manual/Stitching_a_photo-mosaic.html + # https://wiki.panotools.org/Hugin_FAQ - search "translation" + # src_image.setVar("Tpy", yaw * RAD2DEG) + # src_image.setVar("Tpp", pitch * RAD2DEG) + + need_pose = False + + return src_images + + +def get_undistorted_path(fname, undistort_dir, ext): + with_ext = os.path.splitext(os.path.basename(fname))[0] + ext + return os.path.realpath(os.path.join(undistort_dir, with_ext)) + + +def undistort_images(src_images, output_dir): + undistort_dir = os.path.join(output_dir, "build", "undistort") + intrinsics_path = os.path.join(undistort_dir, "undistorted_intrinsics.txt") + + call_undistort(src_images, undistort_dir, intrinsics_path) + + # point pto file at the undistorted images + for img in src_images: + img.setFilename(get_undistorted_path(img.getFilename(), undistort_dir, ".png")) + + undistorted_intrinsics = read_undistorted_intrinsics(intrinsics_path) + set_h_fov(src_images, undistorted_intrinsics) + + +def call_undistort(src_images, undistort_dir, intrinsics_path): + if os.path.exists(intrinsics_path): + print( + "Undistort output %s already exists, not reprocessing images" + % intrinsics_path + ) + return + + # create file listing input images for undistort_image + if not os.path.exists(undistort_dir): + os.makedirs(undistort_dir) + + input_images_path = os.path.join(undistort_dir, "input_images.txt") + with open(input_images_path, "w") as input_list: + for img in src_images: + input_list.write(img.getFilename() + "\n") + + # auto-detect if environment variables weren't specified + fill_if_missing( + "ASTROBEE_RESOURCE_DIR", lambda: catkin_find("astrobee", "resources") + ) + fill_if_missing("ASTROBEE_CONFIG_DIR", lambda: catkin_find("astrobee", "config")) + fill_if_missing("ASTROBEE_WORLD", "iss") + # Note: No default value for robot. The user really needs to specify! + + # check necessary environment variables are defined + for var in UNDISTORT_ENV_VARS: + if os.getenv(var) is None: + raise RuntimeError( + "Environment variable %s must be defined in order for undistort_image to read the correct camera parameters" + % var + ) + + run_cmd( + [ + "rosrun", + "camera", + "undistort_image", + "-image_list", + input_images_path, + "-robot_camera", + "sci_cam", + # default output size is much larger than needed, enable autocrop + "-undistorted_crop_win", + "loose", + "-alpha", + "-cubic", + "-output_directory", + undistort_dir, + "-undistorted_intrinsics", + intrinsics_path, + ] + ) + + +def read_undistorted_intrinsics(intrinsics_path): + with open(intrinsics_path, "r") as intrinsics_stream: + lines = intrinsics_stream.read().splitlines() + vals = lines[1].split() + # all values have units of pixels + intrinsics = { + "width": float(vals[0]), + "height": float(vals[1]), + "focal_length": float(vals[2]), + "center_x": float(vals[3]), + "center_y": float(vals[4]), + } + return intrinsics + + +def get_h_fov(undistorted_intrinsics): + # see https://wiki.panotools.org/Field_of_View "Conversion from focal length" + width = undistorted_intrinsics["width"] + f = undistorted_intrinsics["focal_length"] + h_fov_degrees = RAD2DEG * 2 * math.atan(width / (2 * f)) + return h_fov_degrees + + +def set_h_fov(src_images, undistorted_intrinsics): + h_fov_degrees = get_h_fov(undistorted_intrinsics) + for img in src_images: + img.setVar("v", h_fov_degrees) + + +def concat_if(prefix, suffix, cond): + if cond: + return prefix + suffix + else: + return prefix + + +def filter_params1(params, reject_params, do_filter): + if do_filter: + return ",".join([p for p in params.split(",") if p not in reject_params]) + else: + return params + + +def filter_params(params, args): + params = filter_params1(params, LENS_PARAMS, args.no_lens) + params = filter_params1(params, TRANSLATION_PARAMS, args.no_translation) + return params + + +class PathSequence(object): + """ + Generates a sequence of filenames based on a "base" filename + by inserting a number "_NNN" before the extension. + """ + + def __init__(self, base_path, insert_n): + self.base_path = base_path + self.insert_n = insert_n + self.n = 0 + + def insert_suffix(self, suffix): + prefix, ext = os.path.splitext(self.base_path) + return prefix + suffix + ext + + def get_path(self): + if self.insert_n: + return self.insert_suffix("_%03d" % self.n) + else: + return self.base_path + + def next(self): + self.n += 1 + return self.get_path() + + +def read_pto(pano, pto_path): + print("\nread_pto: %s" % pto_path) + ifs = hsi.ifstream(pto_path) + pano.readData(ifs) + + +def write_pto(pano, pto_path): + print("\nwrite_pto: %s" % pto_path) + ofs = hsi.ofstream(pto_path) + pano.writeData(ofs) + + +def get_timestamp(): + return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + +def duplicate_console_to_log(log_path): + """ + Duplicate console output to specified log file. Both stdout + and stderr of this process and all children should be included. + """ + print("Duplicating console log to %s" % log_path) + log_path = os.path.realpath(log_path) + log_dir = os.path.dirname(log_path) + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + # Unbuffer stdout (ensures stdout and stderr interleave properly). As of + # Python 3.3+ this might not work and no longer be needed. + if sys.version_info < (3, 3): + sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0) + + tee = subprocess.Popen(["tee", "--append", log_path], stdin=subprocess.PIPE) + os.dup2(tee.stdin.fileno(), sys.stdout.fileno()) + os.dup2(tee.stdin.fileno(), sys.stderr.fileno()) + + +def main(): + args = parse_args() + + inbag_base = os.path.splitext(os.path.basename(args.inbag))[0] + output_dir = args.output_dir.format(inbag=inbag_base) + + if not args.no_log: + log_path = os.path.join(output_dir, "build", "console.log") + duplicate_console_to_log(log_path) + + print("%s Started run" % get_timestamp()) + print("Command-line arguments: %s" % sys.argv) + + pto_base = os.path.join(output_dir, "build", "stitch.pto") + pto = PathSequence(pto_base, True) + pto_preremap = pto.insert_suffix("_preremap") + + if not args.only_stitching: + src_images = get_image_meta(args.inbag, args.images_dir, args.skip_images) + if not args.no_undistort: + undistort_images(src_images, output_dir) + + # Make a new Panorama object + p = hsi.Panorama() + for img in src_images: + p.addImage(img) + + # Link lenses + variable_groups = hsi.StandardImageVariableGroups(p) + lenses = variable_groups.getLenses() + for i in range(0, p.getNrOfImages()): + lenses.switchParts(i, lenses.getPartNumber(0)) + + write_pto(p, pto.get_path()) + + # Generate control points + cmd = [ + "cpfind", + "--multirow", + # Request more control points 2 -> 5 in case it helps optimization. + "--sieve2size", + "5", + # Per cpfind man page, rpy is "the preferred mode if a calibrated + # lens is used" + "--ransacmode", + "rpy", + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + # Throw away control points are prob invalid + cmd = ["cpclean", pto.get_path(), "-o", pto.next()] + run_cmd(cmd) + + # Optimize attitude + b + cmd = [ + "pto_var", + "--opt", + filter_params("y,p,r,b", args), + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + # Optimize position iteratively not to diverge + read_pto(p, pto.get_path()) + + orig_tuple = p.getOptimizeVector() + + pano_size = len(orig_tuple) + stride = 5 + optim_nr_list = [10, 15] + + for optim_nr in optim_nr_list: + + # Optimizer cycles + # We do this iteratively otherwise it will diverge + for x in range(0, pano_size, stride): + print(x) + read_pto(p, pto.get_path()) + optvec = [] + for i in range(pano_size): + if (x + optim_nr) > pano_size: + start = x + optim_nr - pano_size + else: + start = 0 + if x <= i < x + optim_nr or i < start: + optvec.append( + filter_params("y,p,r,Tpp,Tpy,TrX,TrY,TrZ", args).split(",") + ) + else: + optvec.append([]) + + print(optvec) + p.setOptimizeVector(optvec) + write_pto(p, pto.next()) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + # Optimize attitude + b + v + cmd = [ + "pto_var", + "--opt", + filter_params("y,p,r,b,v", args), + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + # Optimize attitude + position + cmd = [ + "pto_var", + "--opt", + filter_params("y,p,r,Tpp,Tpy,TrX,TrY,TrZ", args), + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + # Optimize ALL x3 + cmd = [ + "pto_var", + "--opt", + filter_params("y,p,r,Tpp,Tpy,TrX,TrY,TrZ,b,v", args), + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + cmd = [ + "pto_var", + "--opt", + filter_params("y,p,r,TrX,TrY,TrZ,b,v", args), + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + cmd = [ + "pto_var", + "--opt", + filter_params("y,p,r,Tpp,Tpy,TrX,TrY,TrZ,b,v", args), + pto.get_path(), + "-o", + pto.next(), + ] + run_cmd(cmd) + + current = pto.get_path() + cmd = ["autooptimiser", "-n", "-o", pto.next(), current] + run_cmd(cmd) + + # Photometric Optimizer + current = pto.get_path() + cmd = ["autooptimiser", "-m", "-o", pto.next(), current] + run_cmd(cmd) + + # Need to copy last pto from optimization to a standard + # location, so it can be used on any subsequent + # --stitching-only runs. + shutil.copyfile(pto.get_path(), pto_preremap) + + if not args.no_stitching: + print("%s Starting stitching" % get_timestamp()) + + pto_remap_base = os.path.join(output_dir, "build", "stitch_remap.pto") + pto_remap = PathSequence(pto_remap_base, True) + + # copy last file from pto preremap sequence to be the first in + # the pto remap sequence + shutil.copyfile(pto_preremap, pto_remap.get_path()) + + # Configure image output + cmd = [ + "pano_modify", + "--center", + "--canvas=AUTO", + "--projection=2", + "--fov=360x180", + "--output-type=NORMAL,REMAP", + # Use lossless TIFF 'deflate' compression in output + # images. Empirically, this makes them smaller but not as small as + # PNG, so we'll do one more conversion at the end. + "--ldr-compression=DEFLATE", + pto_remap.get_path(), + "-o", + pto_remap.next(), + ] + run_cmd(cmd) + + p = hsi.Panorama() + + # Set enblend options for stitching + read_pto(p, pto_remap.get_path()) + + p.getOptions().enblendOptions += " " + args.enblend_options + + # Black dropout areas workaround #1: Switch primary seam generator to + # less advanced but possibly more robust older version. This is probably + # the better way to handle it (increases speed as well). Some places + # online recommend trying this. + # p.getOptions().enblendOptions += " --primary-seam-generator=nft" + + # Black dropout areas workaround #2: Turn off seam optimization. This + # seems to help in some cases but not as often? May not be needed if + # workaround #1 is used. + # p.getOptions().enblendOptions += " --no-optimize --fine-mask" + + print("Set enblend options: %s" % p.getOptions().enblendOptions) + write_pto(p, pto_remap.next()) + + pto_final = pto.insert_suffix("_final") + shutil.copyfile(pto_remap.get_path(), pto_final) + print("Wrote final stitching metadata to %s" % pto_final) + + # Generate panorama + pano_path = os.path.join(output_dir, "build", "pano") + cmd = [ + "hugin_executor", + "--stitching", + "--prefix=" + pano_path, + pto_final, + ] + + print("\n=== Stitching first in dry run mode for debugging ===") + dry_run_cmd = list(cmd) + dry_run_cmd[-1:-1] = ["--dry-run"] + run_cmd(dry_run_cmd) + + print("\n=== Now stitching for real ===") + run_cmd(cmd) + + tif_path = pano_path + ".tif" + final_png_path = os.path.join(output_dir, os.path.basename(pano_path)) + ".png" + print("opencv_convert %s %s" % (tif_path, final_png_path)) + pano_img = cv2.imread(tif_path) + cv2.imwrite(final_png_path, pano_img) + + print( + "Preserving final metadata %s alongside output pano image" + % os.path.basename(pto_final) + ) + shutil.copyfile(pto_final, output_dir) + + print("\n=== Final stitched pano in %s ===\n" % final_png_path) + + print("%s Finished run" % get_timestamp()) + if not args.no_log: + print("Complete console output in %s" % log_path) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_view/CMakeLists.txt b/pano/pano_view/CMakeLists.txt new file mode 100644 index 00000000..85b56067 --- /dev/null +++ b/pano/pano_view/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2021, 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. + +# Check to see if correct version of media has been downloaded into tree. We +# dont do this if we are cross-compiling, as there is no need for it. We must +# also make sure the media is copied in a native install to a simulator. + +cmake_minimum_required(VERSION 2.8.3) +project(pano_view) + +find_package(catkin REQUIRED COMPONENTS) + +catkin_package() diff --git a/pano/pano_view/media/arrow.png b/pano/pano_view/media/arrow.png new file mode 100644 index 00000000..caa48390 Binary files /dev/null and b/pano/pano_view/media/arrow.png differ diff --git a/pano/pano_view/media/camera.png b/pano/pano_view/media/camera.png new file mode 100644 index 00000000..c607183d Binary files /dev/null and b/pano/pano_view/media/camera.png differ diff --git a/pano/pano_view/media/map.png b/pano/pano_view/media/map.png new file mode 100644 index 00000000..fc400683 Binary files /dev/null and b/pano/pano_view/media/map.png differ diff --git a/pano/pano_view/package.xml b/pano/pano_view/package.xml new file mode 100644 index 00000000..9a37d6cb --- /dev/null +++ b/pano/pano_view/package.xml @@ -0,0 +1,17 @@ + + + pano_view + 0.0.0 + The pano_view package + + Apache License, Version 2.0 + + + ISAAC Flight Software + + + ISAAC Flight Software + + + catkin + diff --git a/pano/pano_view/scripts/Snakefile b/pano/pano_view/scripts/Snakefile new file mode 100644 index 00000000..5253d49f --- /dev/null +++ b/pano/pano_view/scripts/Snakefile @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Snakemake rules for batch stitching multiple panoramas. +""" + +from dot_dict import DotDict + +# When using this Snakefile, use the --directory option to snakemake to set the +# desired output directory (e.g., "/output"). + +configfile: "pano_meta.yaml" + +rule all: + input: + "html/tour.json" + +# We set up an explicit check if the output pano exists before +# restitching. This avoids unwanted restitching of all panos based on +# irrelevant changes to the Snakefile or configfile. Explicitly delete +# the output pano.png file if the pano needs to be restitched. + +rule stitch: + output: + protected("stitch/{scene_id}/pano.png") + params: + scene=lambda wildcards: DotDict(config["scenes"][wildcards.scene_id]) + shell: + ("[ -f {output} ] ||" + " rosrun pano_stitch stitch_panorama.py" + " --no-lens" + " --robot={params.scene.robot}" + " --images-dir={params.scene.images_dir}" + " {params.scene.bag_path}" + " --output-dir=stitch/{wildcards.scene_id}" + " {params.scene.extra_stitch_args}") + +# Note we have to remove the output directory before running +# generate.py because Snakemake tries to help by creating it, but +# generate.py aborts if the directory exists to avoid accidentally +# overwriting previous output. + +# As with stitching, if you want to force rebuild you should +# explicitly delete the rule's output file (config.json). + +rule tile: + input: + "stitch/{scene_id}/pano.png" + output: + protected("html/scenes/{scene_id}/config.json") + shell: + ("[ -f {output} ] ||" + " rmdir $(dirname {output}) && " + " /opt/pannellum/utils/multires/generate.py" + " {input}" + " -o html/scenes/{wildcards.scene_id}") + +rule prep_source_images: + input: + expand("stitch/{scene_id}/pano.png", scene_id=config["scenes"].keys()) + output: + touch("html/source_images/completed.txt") + shell: + "rosrun pano_view prep_source_images.py" + +rule tour: + input: + expand("html/scenes/{scene_id}/config.json", scene_id=config["scenes"].keys()), + "html/source_images/completed.txt" + output: + "html/tour.json" + shell: + "rosrun pano_view generate_tour.py" diff --git a/pano/pano_view/scripts/dot_dict.py b/pano/pano_view/scripts/dot_dict.py new file mode 100644 index 00000000..1c669b57 --- /dev/null +++ b/pano/pano_view/scripts/dot_dict.py @@ -0,0 +1,31 @@ +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Convenience wrapper for dict so you can access members with dot notation. +""" + + +class DotDict(dict): + """ + Convenience wrapper for dict so you can access members with dot notation. + """ + + def __getattr__(self, attr): + if attr in self: + return self[attr] + return super(DotDict, self).__getattribute__(attr) diff --git a/pano/pano_view/scripts/generate_tour.py b/pano/pano_view/scripts/generate_tour.py new file mode 100755 index 00000000..a0d7b836 --- /dev/null +++ b/pano/pano_view/scripts/generate_tour.py @@ -0,0 +1,509 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Generate a panoramic tour from stitched and tiled panos. +""" + +import argparse +import json +import os +import re + +import numpy as np +import scipy.sparse.csgraph +import scipy.spatial.distance +import yaml + +PANO_VIEW_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +PACKAGES = [ + "pannellum", + "openseadragon", + "annotorious-openseadragon", + "annotorious-selectorpack", + "annotorious-toolbar", +] +DEFAULT_PACKAGE_PATHS = {pkg: "/opt/" + pkg for pkg in PACKAGES} + +TOUR_DEFAULT_INIT = { + "sceneFadeDuration": 1000, +} + +TOUR_SCENE_INIT = { + "title": "ISS {module} {bay}", + "author": "{author} - captured {date}", + "type": "multires", + "hfov": 150.0, + "autoLoad": True, +} + +SCENE_META_INIT = { + "author": "NASA ISAAC Project", +} + +SCENE_LINK_HOT_SPOT_TEXT = "{module} {bay}" + +# Pick a default starting yaw for each module. Pointing along the centerline +# to start feels more natural. +CENTERLINE_YAW = { + "jem": 90, + "nod2": 180, + "col": 90, + "usl": 180, + "nod1": 180, +} + +MULTI_SPACE_REGEX = re.compile(r" +") + +DEGREES_PER_RADIAN = 180 / np.pi + +# This is very roughly calibrated. Note: Overview map may not be exactly to scale! +OVERVIEW_PX_PER_METER = 8.63 +OVERVIEW_X0_PX = 118 + OVERVIEW_PX_PER_METER * 1.64 +OVERVIEW_Y0_PX = 118 + + +def dosys(cmd, exit_on_error=True): + print("+ " + cmd) + ret = os.system(cmd) + if ret != 0: + msg = "Command exited with non-zero return value %s" % ret + if exit_on_error: + raise RuntimeError(msg) + print(msg) + return ret + + +def install_glob(src_glob, tgt_folder): + if not os.path.isdir(tgt_folder): + dosys("mkdir -p %s" % tgt_folder) + dosys("cp -r %s %s" % (src_glob, tgt_folder)) + + +def install_file(src_path, tgt_folder, tgt_name=None): + if not os.path.isdir(tgt_folder): + dosys("mkdir -p %s" % tgt_folder) + if tgt_name is None: + tgt_path = tgt_folder + else: + tgt_path = os.path.join(tgt_folder, tgt_name) + dosys("cp -r %s %s" % (src_path, tgt_path)) + + +def install_static_files(out_folder, package_paths): + pannellum_path = package_paths["pannellum"] + install_glob( + os.path.join(pannellum_path, "build/pannellum.js"), + os.path.join(out_folder, "js"), + ) + install_glob( + os.path.join(pannellum_path, "build/pannellum.css"), + os.path.join(out_folder, "css"), + ) + install_glob( + os.path.join(pannellum_path, "src/standalone/standalone.js"), + os.path.join(out_folder, "js"), + ) + install_glob( + os.path.join(pannellum_path, "src/standalone/standalone.css"), + os.path.join(out_folder, "css"), + ) + + openseadragon_path = package_paths["openseadragon"] + install_glob( + os.path.join(openseadragon_path, "openseadragon.min.js*"), + os.path.join(out_folder, "js"), + ) + install_glob( + os.path.join(openseadragon_path, "images/*"), + os.path.join(out_folder, "media/openseadragon"), + ) + + anno_path = package_paths["annotorious-openseadragon"] + install_glob( + os.path.join(anno_path, "*.js"), + os.path.join(out_folder, "js"), + ) + install_glob( + os.path.join(anno_path, "*.js.map"), + os.path.join(out_folder, "js"), + ) + install_glob( + os.path.join(anno_path, "*.css"), + os.path.join(out_folder, "css"), + ) + + sel_path = package_paths["annotorious-selectorpack"] + install_glob( + os.path.join(sel_path, "*.js"), + os.path.join(out_folder, "js"), + ) + install_glob( + os.path.join(sel_path, "*.js.map"), + os.path.join(out_folder, "js"), + ) + + toolbar_path = package_paths["annotorious-toolbar"] + install_glob( + os.path.join(toolbar_path, "annotorious-toolbar.min.js"), + os.path.join(out_folder, "js"), + ) + + install_glob( + os.path.join(PANO_VIEW_ROOT, "static/js/*"), os.path.join(out_folder, "js") + ) + install_glob( + os.path.join(PANO_VIEW_ROOT, "static/css/*"), os.path.join(out_folder, "css") + ) + install_glob( + os.path.join(PANO_VIEW_ROOT, "media/*"), os.path.join(out_folder, "media") + ) + + # Some HTML files in the templates folder are currently static so + # we can just install them; in the future we might replace them + # with templates to provide greater flexibility, which would require + # template rendering. + install_glob(os.path.join(PANO_VIEW_ROOT, "templates/pannellum.htm"), out_folder) + install_file( + os.path.join(PANO_VIEW_ROOT, "templates/isaac_source_image.html"), + os.path.join(out_folder, "src"), + "index.html", + ) + + +def get_display_scene_meta(scene_id, config_scene_meta): + scene_meta = SCENE_META_INIT.copy() + scene_meta.update(config_scene_meta) + + scene_meta["scene_id"] = scene_id + + # Replace missing or null field with "" to avoid errors + for field in ("activity", "module", "bay", "date"): + if scene_meta.get(field) is None: + scene_meta[field] = "" + + # Prettify some meta fields for use in templates + if "activity" in scene_meta: + scene_meta["activity"] = scene_meta["activity"].upper() + if "module" in scene_meta: + scene_meta["module"] = scene_meta["module"].upper() + if "bay" in scene_meta: + scene_meta["bay"] = "Bay %s" % scene_meta["bay"] + if "end_time" in scene_meta: + scene_meta["date"] = scene_meta["end_time"].split("T", 1)[0] + + return scene_meta + + +def fill_field(tmpl, display_scene_meta): + # Replace template {patterns} with variable values from display_scene_meta + val = tmpl.format(**display_scene_meta) + # Collapse multiple spaces to single space and strip leading and + # trailing whitespace. (Templates have space-separated fields and + # some fields may be empty... removing extra spaces looks better.) + return MULTI_SPACE_REGEX.sub(" ", val).strip() + + +def get_angles0(p_from, p_to): + """ + Return yaw and pitch angles that will point a camera at p_from to + a target at p_to. Both arguments are 3D points. + """ + d = p_to - p_from + return { + "yaw": np.arctan2(d[1], d[0]) * DEGREES_PER_RADIAN, + "pitch": np.arctan2(d[2], np.sqrt(d[0] ** 2 + d[1] ** 2)) * DEGREES_PER_RADIAN, + } + + +def get_angles(p_from, p_to, module_from, force_centerline=False): + """ + Return yaw and pitch angles that will point a camera at p_from to + a target at p_to. Both arguments are 3D points. If + force_centerline is True, a module is defined for p_from, and a + centerline yaw is defined for that module, force the hot spot + angles to exactly align with the module centerline in whichever + direction is nearer to the target yaw. + """ + angles = get_angles0(p_from, p_to) + module_yaw = CENTERLINE_YAW.get(module_from) + + if module_yaw is None or not force_centerline: + return angles + + diff = abs(angles["yaw"] - module_yaw) + if diff < 180: + yaw = module_yaw + else: + yaw = (module_yaw + 180) % 360 + return { + "yaw": yaw, + "pitch": 0, + } + + +def get_overview_map_position(scene_meta): + p = scene_meta["position"] + x = p["x"] + y = p["y"] + return ( + OVERVIEW_Y0_PX + y * OVERVIEW_PX_PER_METER, + OVERVIEW_X0_PX - x * OVERVIEW_PX_PER_METER, + ) + + +def link_scenes(config, tour_scenes): + # Collect positions of scenes + n = len(config["scenes"]) + pos = np.zeros((n, 3)) + for i, config_scene_meta in enumerate(config["scenes"].values()): + p = config_scene_meta["position"] + pos[i, :] = (p["x"], p["y"], p["z"]) + + # Calculate Euclidean distance cost matrix M between scenes. + # M_ij is the Euclidean distance between scene i and scene j. + cost_matrix = scipy.spatial.distance.cdist(pos, pos, "euclidean") + + # Calculate a minimum spanning tree that spans all scenes. Each + # edge in the MST between two scenes will turn into a two-way + # hot-spot link between the scenes. Because the MST spans all + # scenes, you should be able to use the links to reach any scene + # from any other scene. Because we tend to capture panoramas on + # ISS module centerlines and the centerlines form a tree, with + # luck using the MST as a heuristic will give us a topology that + # matches the ISS module topology and will seem natural to users. + tree = scipy.sparse.csgraph.minimum_spanning_tree(cost_matrix) + + scene_id_lookup = list(config["scenes"].keys()) + + for j1, j2 in np.transpose(np.nonzero(tree)): + for j_from, j_to in ((j1, j2), (j2, j1)): + scene_id_from = scene_id_lookup[j_from] + config_scene_meta_from = config["scenes"][scene_id_from] + tour_scene_from = tour_scenes[scene_id_from] + + scene_id_to = scene_id_lookup[j_to] + config_scene_meta_to = config["scenes"][scene_id_to] + scene_meta_to = get_display_scene_meta(scene_id_to, config_scene_meta_to) + tour_scene_to = tour_scenes[scene_id_to] + + angles = get_angles( + pos[j_from, :], pos[j_to, :], config_scene_meta_from.get("module") + ) + hot_spot = { + "type": "scene", + "id": scene_id_to, + "sceneId": scene_id_to, + "text": fill_field(SCENE_LINK_HOT_SPOT_TEXT, scene_meta_to), + "yaw": angles["yaw"] - tour_scene_from.get("northOffset", 0), + "pitch": angles["pitch"], + "targetYaw": angles["yaw"] - tour_scene_to.get("northOffset", 0), + "targetPitch": angles["pitch"], + } + + hot_spots = tour_scene_from.setdefault("hotSpots", []) + hot_spots.append(hot_spot) + + +def link_source_images(config, tour_scenes, out_folder): + for scene_id, config_scene_meta in config["scenes"].items(): + tour_scene = tour_scenes[scene_id] + hot_spots = tour_scene.setdefault("hotSpots", []) + + src_images_meta_path = os.path.join( + out_folder, "source_images", scene_id, "meta.json" + ) + + if not os.path.exists(src_images_meta_path): + print("warning: no source images prepped for scene %s" % scene_id) + continue + + with open(src_images_meta_path, "r") as src_images_meta_stream: + src_images_meta = json.load(src_images_meta_stream) + + img_ids = sorted(src_images_meta.keys()) + for i, img_id in enumerate(img_ids): + img_meta = src_images_meta[img_id] + hot_spots.append( + { + "type": "info", + "id": i, + "text": "Image %d" % i, + "yaw": img_meta["yaw"] - tour_scene.get("northOffset", 0), + "pitch": img_meta["pitch"], + "URL": "src/#scene=%s&imageId=%s" % (scene_id, img_id), + "cssClass": "isaac-source-image pnlm-hotspot pnlm-sprite", + "attributes": { + "target": "_blank", + }, + } + ) + + +def generate_tour_json(config, out_folder): + tour_default = TOUR_DEFAULT_INIT.copy() + tour_default["firstScene"] = next(iter(config["scenes"].keys())) + tour = {"default": tour_default} + + tour_scenes = {} + tour["scenes"] = tour_scenes + + for scene_id, config_scene_meta in config["scenes"].items(): + # Read tiler scene metadata + tiler_meta_path = os.path.join(out_folder, "scenes", scene_id, "config.json") + config_scene_meta["tiled"] = os.path.isfile(tiler_meta_path) + if not config_scene_meta["tiled"]: + print("warning: skipping %s (%s not found)" % (scene_id, tiler_meta_path)) + continue + with open(tiler_meta_path, "r") as tiler_meta_stream: + tiler_meta = json.load(tiler_meta_stream) + + scene_meta = get_display_scene_meta(scene_id, config_scene_meta) + + tour_scene = TOUR_SCENE_INIT.copy() + + # Fill tour_scene fields that are templates + tour_scene_updates = {} + for field, val in tour_scene.items(): + if isinstance(val, str) and "{" in val: + tour_scene_updates[field] = fill_field(val, scene_meta) + tour_scene.update(tour_scene_updates) + + # Copy tiler metadata into scene. Paths output by the tiler + # start with "/" but are actually relative to the tiler output + # dir, so we need to strip the leading slash and prepend the + # path from tour.json to the tiler output dir. + multi_res_meta = tiler_meta["multiRes"] + multi_res_meta["path"] = os.path.join( + "scenes", scene_id, multi_res_meta["path"][1:] + ) + multi_res_meta["fallbackPath"] = os.path.join( + "scenes", scene_id, multi_res_meta["fallbackPath"][1:] + ) + tour_scene["multiRes"] = multi_res_meta + + tour_scene["yaw"] = CENTERLINE_YAW.get(config_scene_meta["module"], 0) + tour_scene["overviewMapPosition"] = get_overview_map_position(scene_meta) + + extra_tour_params = config_scene_meta.get("extra_tour_params", {}) + tour_scene.update(extra_tour_params) + + # We need to adjust the yaw for northOffset if it is used. + north_offset = tour_scene.get("northOffset", 0) + tour_scene["yaw"] -= north_offset + + # Add scene to the tour. + tour_scenes[scene_id] = tour_scene + + link_scenes(config, tour_scenes) + link_source_images(config, tour_scenes, out_folder) + + out_path = os.path.join(out_folder, "tour.json") + with open(out_path, "w") as out: + json.dump(tour, out, indent=4) + print("wrote to %s" % out_path) + + +def generate_scene_index(config, out_folder): + # Can improve this to be grouped by modules and bays sorted in increasing order. + index = ["
    "] + for scene_id, config_scene_meta in config["scenes"].items(): + if not config_scene_meta["tiled"]: + continue + + scene_meta = get_display_scene_meta(scene_id, config_scene_meta) + index.append( + fill_field( + ( + '
  • ' + "{module} {bay}" + "
  • " + ), + scene_meta, + ) + ) + index.append("
") + index_str = "\n".join(index) + "\n" + + # HACK replace this with a real templating system like Jinja2 + template_path = os.path.join(PANO_VIEW_ROOT, "templates/index.html") + with open(template_path, "r") as template_stream: + template = template_stream.read() + html = template.replace("{{ index }}", index_str) + + out_path = os.path.join(out_folder, "index.html") + with open(out_path, "w") as out: + out.write(html) + print("wrote to %s" % out_path) + + +def generate_tour(config_path, out_folder, package_paths): + with open(config_path, "r") as config_stream: + config = yaml.safe_load(config_stream) + + install_static_files(out_folder, package_paths) + generate_tour_json(config, out_folder) + generate_scene_index(config, out_folder) + dosys("chmod a+rX %s" % out_folder) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "-c", + "--config", + type=str, + help="input path for YAML pano stitch config", + default="/output/pano_meta.yaml", + required=False, + ) + parser.add_argument( + "-o", + "--out-folder", + type=str, + help="output path for static web files of tour", + default="/output/html", + required=False, + ) + parser.add_argument( + "--package-paths", + type=str, + help="comma-separated list of package paths to override formatted like 'openseadragon=/opt/openseadragon'", + default=None, + required=False, + ) + + args = parser.parse_args() + + package_paths = DEFAULT_PACKAGE_PATHS.copy() + if args.package_paths is not None: + updates = [assn.split("=", 1) for assn in args.package_paths.split(",")] + package_paths.update(dict(updates)) + generate_tour(args.config, args.out_folder, package_paths) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_view/scripts/prep_source_images.py b/pano/pano_view/scripts/prep_source_images.py new file mode 100755 index 00000000..d617773a --- /dev/null +++ b/pano/pano_view/scripts/prep_source_images.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. + +""" +Prep source images for web viewing (so panorama can link to them). +""" + +import argparse +import collections +import functools +import json +import multiprocessing as mp +import operator +import os + +import hsi +import yaml + + +def dosys(cmd, exit_on_error=True): + print("+ " + cmd) + ret = os.system(cmd) + if ret != 0: + msg = "Command exited with non-zero return value %s" % ret + if exit_on_error: + raise RuntimeError(msg) + print(msg) + return ret + + +def read_pto(pto_path): + pano = hsi.Panorama() + ifs = hsi.ifstream(pto_path) + pano.readData(ifs) + return pano + + +def read_scene_source_images_meta(stitch_folder, scene_id): + pto_path = os.path.join(stitch_folder, scene_id, "stitch_final.pto") + pano = read_pto(pto_path) + num_images = pano.getNrOfImages() + images_meta = {} + for i in range(num_images): + img = pano.getImage(i) + img_id = os.path.splitext(os.path.basename(img.getFilename()))[0] + images_meta[img_id] = { + "yaw": img.getYaw(), + "pitch": img.getPitch(), + } + + return images_meta + + +def do_prep_image(job_args): + image_in, dz_out = job_args + + dz_out_parent = os.path.dirname(dz_out) + if not os.path.exists(dz_out_parent): + dosys("mkdir -p %s" % dz_out_parent) + + partial_paths = [ + "%s_partial.dzi" % dz_out, + "%s_partial_files" % dz_out, + ] + if any((os.path.exists(p) for p in partial_paths)): + dosys("rm -rf %s" % (" ".join(partial_paths))) + + dosys("vips dzsave %s %s_partial" % (image_in, dz_out)) + for p in partial_paths: + dosys("mv %s %s" % (p, p.replace("_partial", ""))) + + +def write_images_meta(images_meta, meta_out_path): + meta_out_path_parent = os.path.dirname(meta_out_path) + if not os.path.exists(meta_out_path_parent): + dosys("mkdir -p %s" % meta_out_path_parent) + + with open(meta_out_path, "w") as meta_out: + json.dump(images_meta, meta_out, indent=4) + print("wrote %s" % meta_out_path) + + +def get_scene_q(config, stitch_folder, out_folder, scene_id): + images_meta = read_scene_source_images_meta(stitch_folder, scene_id) + scene_meta = config["scenes"][scene_id] + scene_out = os.path.join(out_folder, "source_images", scene_id) + + prep_image_q = [] + for img_id in images_meta.keys(): + image_in = os.path.join(scene_meta["images_dir"], img_id + ".jpg") + dz_out = os.path.join(scene_out, img_id) + if os.path.exists(dz_out + ".dzi"): + continue + prep_image_q.append((image_in, dz_out)) + + print( + "%s: %s out of %s source images need prep" + % (scene_id, len(prep_image_q), len(images_meta)) + ) + + meta_out_path = os.path.join(scene_out, "meta.json") + write_images_meta(images_meta, meta_out_path) + + return prep_image_q + + +def execute_q(pool, do_job, q): + result_iterator = pool.imap_unordered(do_job, q) + # exhaust iterator + collections.deque(result_iterator, maxlen=0) + + +def join_lists(lists): + return functools.reduce(operator.iadd, lists, []) + + +def prep_source_images(config_path, stitch_folder, out_folder, num_jobs): + with open(config_path, "r") as config_stream: + config = yaml.safe_load(config_stream) + + prep_image_q = join_lists( + ( + get_scene_q(config, stitch_folder, out_folder, scene_id) + for scene_id in config["scenes"].keys() + ) + ) + + print( + "queued image prep jobs: %s total jobs for %s scenes" + % (len(prep_image_q), len(config["scenes"])) + ) + + print("executing job queue") + with mp.Pool(processes=num_jobs) as pool: + execute_q(pool, do_prep_image, prep_image_q) + + print( + "executed prep jobs: %s total jobs for %s scenes" + % (len(prep_image_q), len(config["scenes"])) + ) + + +class CustomFormatter( + argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter +): + pass + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=CustomFormatter + ) + parser.add_argument( + "-c", + "--config", + type=str, + help="input path for YAML pano stitch config", + default="/output/pano_meta.yaml", + required=False, + ) + parser.add_argument( + "-s", + "--stitch-folder", + type=str, + help="input path for pano stitching results", + default="/output/stitch", + required=False, + ) + parser.add_argument( + "-o", + "--out-folder", + type=str, + help="output path for static web files", + default="/output/html", + required=False, + ) + parser.add_argument( + "-j", + "--jobs", + type=int, + help="number of image processing jobs to run in parallel", + default=mp.cpu_count() // 2, + required=False, + ) + + args = parser.parse_args() + + prep_source_images(args.config, args.stitch_folder, args.out_folder, args.jobs) + + +if __name__ == "__main__": + main() diff --git a/pano/pano_view/static/css/isaac_pano.css b/pano/pano_view/static/css/isaac_pano.css new file mode 100644 index 00000000..dfa5a280 --- /dev/null +++ b/pano/pano_view/static/css/isaac_pano.css @@ -0,0 +1,148 @@ +/* Copyright (c) 2021, 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. + */ + +/* Default to sans-serif where no font is specified. */ + +body { + font-family: sans-serif; +} + +/********************************************************************** + * Overview map + **********************************************************************/ + +.pnlm-overview-map { + position: absolute; + width: 184px; + height: 200px; + right: 4px; + top: 4px; + border-radius: 4px; + background: url('../media/map.png') no-repeat #fff; + background-origin: content-box; + padding: 8px; + cursor: default; + display: inline; +} + +.pnlm-map-marker { + position: absolute; + width: 4px; + height: 4px; + border-radius: 2px; + background-color: #ccc; + transform: translate(-50%, -50%); + z-index: 10; +} + +.pnlm-map-highlight { + position: absolute; + width: 8px; + height: 8px; + border-radius: 4px; + background-color: #000; + transform: translate(-50%, -50%); + z-index: 15; + display: none; +} + +.pnlm-map-current { + position: absolute; + width: 18px; + height: 18px; + background: url('../media/arrow.png'); + transform: translate(-50%, -50%); + z-index: 5; +} + +/********************************************************************** + * Source image custom hot spot and tune Pannellum hot spot style + **********************************************************************/ + +.isaac-source-image { + background-image: url('../media/camera.png') !important; + background-position: 0 0px !important; +} + +/* make this wider so scene link tooltips don't have to wrap */ +div.pnlm-tooltip span { + max-width: 300px; +} + +/********************************************************************** + * Nav bar and view options dropdown menu + **********************************************************************/ + +.isaac-navbar { + padding: 5px; +} + +.isaac-drop-button { + background-color: #ddd; + color: #666; + padding: 6px; + font-size: 12px; + cursor: pointer; + border: 1px solid #666; +} + +.isaac-drop-button:hover, .isaac-drop-button:focus { + background-color: #ddd; + color: black; +} + +.isaac-dropdown { + position: relative; + display: inline-block; +} + +.isaac-dropdown-content { + display: none; + position: absolute; + background-color: #fff; + min-width: 200px; + overflow: auto; + border: 1px solid black; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; +} + +.isaac-dropdown-content .isaac-toggle-entry { + font-size: 12px; + color: black; + padding: 3px 4px; + text-decoration: none; + display: block; +} + +.isaac-dropdown .isaac-toggle-entry:hover { + background-color: #ddd; +} + +.isaac-dropdown .isaac-toggle-entry .checkbox:before { + content: "\2610"; +} + +.isaac-dropdown .isaac-toggle-entry.checked .checkbox:before { + content: "\2611"; +} + +.isaac-dropdown .show { + display: block; +} diff --git a/pano/pano_view/static/js/isaac_pano.js b/pano/pano_view/static/js/isaac_pano.js new file mode 100644 index 00000000..bca475a8 --- /dev/null +++ b/pano/pano_view/static/js/isaac_pano.js @@ -0,0 +1,242 @@ +/* Copyright (c) 2021, 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. + */ + +/********************************************************************** + * Overview map for panoramic tour + **********************************************************************/ + +function getDist(pos0, pos1) { + return Math.sqrt((pos0[0] - pos1[0]) ** 2 + (pos0[1] - pos1[1]) ** 2); +} + +function getClosestScene(pos, config, maxDistance) { + var closestSceneName = null; + var closestSceneDist = 99e+20; + for (let [sceneName, scene] of Object.entries(config.scenes)) { + var dist = getDist(pos, scene.overviewMapPosition); + if (dist <= maxDistance && dist < closestSceneDist) { + closestSceneName = sceneName; + closestSceneDist = dist; + } + } + return closestSceneName; +} + +function getMousePos(event) { + var mapOriginX = window.overviewMap.offsetLeft; + var mapOriginY = window.overviewMap.offsetTop; + var offsetX = event.clientX - mapOriginX; + var offsetY = event.clientY - mapOriginY; + return [offsetX, offsetY]; +} + +function overviewMapClick(event) { + var sceneName = getClosestScene(getMousePos(event), + window.initialConfig, 50); + if (sceneName) { + window.viewer.loadScene(sceneName); + } +} + +function overviewMapMouseLeave(event) { + window.mapHighlight.style.display = "none"; +} + +function overviewMapMouseMove(event) { + var sceneName = getClosestScene(getMousePos(event), + window.initialConfig, 50); + if (sceneName) { + var pos = window.initialConfig.scenes[sceneName].overviewMapPosition; + window.mapHighlight.style.left = pos[0] + "px"; + window.mapHighlight.style.top = pos[1] + "px"; + window.mapHighlight.style.display = "inline"; + } else { + window.mapHighlight.style.display = "none"; + } +} + +function updateMapCurrent() { + var pos = window.viewer.getConfig().overviewMapPosition; + var mapCurrent = window.mapCurrent; + mapCurrent.style.left = pos[0] + "px"; + mapCurrent.style.top = pos[1] + "px"; + + updateYaw(); +} + +function updateYaw() { + var yaw = window.viewer.getConfig().yaw; + var mapCurrent = window.mapCurrent; + var northOffset = window.viewer.getConfig().northOffset || 0; + mapCurrent.style.transform = ( + "translate(-50%, -50%) " + + "rotate(" + (yaw + northOffset) + "deg) " + ); +} + +function initIsaacPano(event) { + var config = event.configFromURL; + window.initialConfig = config; + + var uiContainer = document.getElementsByClassName('pnlm-ui')[0]; + + var overviewMap = document.createElement('div'); + overviewMap.className = 'pnlm-overview-map pnlm-controls pnlm-control'; + uiContainer.appendChild(overviewMap); + window.overviewMap = overviewMap; + + overviewMap.addEventListener('click', overviewMapClick); + overviewMap.addEventListener('mousemove', overviewMapMouseMove); + overviewMap.addEventListener('mouseleave', overviewMapMouseLeave); + + for (let [sceneName, scene] of Object.entries(config.scenes)) { + var mapMarker = document.createElement('div'); + mapMarker.className = 'pnlm-map-marker'; + var pos = scene.overviewMapPosition; + mapMarker.style.left = pos[0] + "px"; + mapMarker.style.top = pos[1] + "px"; + overviewMap.appendChild(mapMarker); + } + + var mapHighlight = document.createElement('div'); + mapHighlight.className = 'pnlm-map-highlight'; + overviewMap.appendChild(mapHighlight); + window.mapHighlight = mapHighlight; + + var mapCurrent = document.createElement('div'); + mapCurrent.className = 'pnlm-map-current'; + overviewMap.appendChild(mapCurrent); + window.mapCurrent = mapCurrent; + + updateMapCurrent(); + + window.viewer.on("scenechange", updateMapCurrent); + window.viewer.on("mouseup", updateYaw); + window.viewer.on("touchend", updateYaw); +} + +document.addEventListener('pannellumloaded', initIsaacPano, false); + +/********************************************************************** + * View options dropdown menu + **********************************************************************/ + +function isaacSleep(secs) { + return new Promise(resolve => setTimeout(resolve, secs * 1000)); +} + +/* When the user clicks on the button, toggle between hiding and showing the dropdown content */ +function isaacToggleDropDown() { + document.getElementById("isaac-view-dropdown").classList.toggle("show"); +} + +function isaacSetVisibility(className, visibility) { + var elts = document.getElementsByClassName(className) + var newDisplayStyle = visibility ? 'block' : 'none'; + for (const elt of elts) { + elt.style.display = newDisplayStyle; + } +} + +function isaacShowNavControls(visibility) { + isaacSetVisibility('pnlm-controls-container', visibility); +} + +function isaacShowOverviewMap(visibility) { + isaacSetVisibility('pnlm-overview-map', visibility); +} + +function isaacShowHotSpotType(hotSpotType, visibility) { + var config = window.viewer.getConfig(); + var currentSceneId = window.viewer.getScene(); + + // Update hotSpots for all scenes. That way the visibility change + // will persist when the scene changes. + for (let [sceneId, scene] of Object.entries(config.scenes)) { + // back up original complete hotSpots array if needed + if (!scene.hasOwnProperty('initialHotSpots')) { + scene.initialHotSpots = [...scene.hotSpots]; + } + + if (visibility) { + // add hotSpots matching type + for (const hotSpot of scene.initialHotSpots) { + if (hotSpot.type == hotSpotType) { + // console.log("window.viewer.addHotSpot(" + hotSpot.id + "," + sceneId + ");"); + window.viewer.addHotSpot(hotSpot, sceneId); + } + } + } else { + // remove hotSpots matching type + var hotSpotsCopy = [...scene.hotSpots]; + for (const hotSpot of hotSpotsCopy) { + if (hotSpot.type == hotSpotType) { + // console.log("window.viewer.removeHotSpot(" + hotSpot.id + "," + sceneId + ");"); + window.viewer.removeHotSpot(hotSpot.id, sceneId); + } + } + } + } +} + +function isaacShowSceneLinks(visibility) { + isaacShowHotSpotType("scene", visibility); +} + +function isaacShowSourceImageLinks(visibility) { + isaacShowHotSpotType("info", visibility); +} + +const ISAAC_CHANGE_VISIBILITY_HANDLERS = { + "isaac-show-nav-controls": isaacShowNavControls, + "isaac-show-overview-map": isaacShowOverviewMap, + "isaac-show-scene-links": isaacShowSceneLinks, + "isaac-show-source-image-links": isaacShowSourceImageLinks, +}; + +function isaacToggleEntryCheckBox(event) { + var elt = event.srcElement; + elt.classList.toggle("checked"); + var visibility = elt.classList.contains("checked"); + ISAAC_CHANGE_VISIBILITY_HANDLERS[elt.id](visibility); +} + +function isaacInitViewDropDown() { + document.getElementsByClassName("isaac-drop-button")[0].onclick = isaacToggleDropDown; + + var toggleEntries = document.getElementsByClassName("isaac-toggle-entry"); + for (const entry of toggleEntries) { + entry.onclick = isaacToggleEntryCheckBox; + } + + // Close the dropdown if the user clicks outside of it + window.onclick = async function(event) { + if (!event.target.matches('.isaac-drop-button')) { + var dropdowns = document.getElementsByClassName("isaac-dropdown-content"); + for (const dropdown of dropdowns) { + if (dropdown.classList.contains('show')) { + await isaacSleep(0.4); + dropdown.classList.remove('show'); + } + } + } + } +} + +isaacInitViewDropDown(); diff --git a/pano/pano_view/static/js/isaac_source_image.js b/pano/pano_view/static/js/isaac_source_image.js new file mode 100644 index 00000000..c1d24588 --- /dev/null +++ b/pano/pano_view/static/js/isaac_source_image.js @@ -0,0 +1,330 @@ +/* Copyright (c) 2021, 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. + */ + +/* View source image with OpenSeaDragon */ + +function parseUrlParameters() { + var url; + if (window.location.hash.length > 0) { + // Prefered method since parameters aren't sent to server + url = window.location.hash.slice(1); + } else { + url = window.location.search.slice(1); + } + if (!url) { + // Display error if no configuration parameters are specified + anError('No configuration options were specified.'); + return; + } + url = url.split('&'); + var configFromUrl = {}; + for (var i = 0; i < url.length; i++) { + var option = url[i].split('=')[0]; + var value = url[i].split('=')[1]; + if (value == '') + continue; // Skip options with empty values in url config + switch(option) { + case 'scene': + case 'imageId': + configFromUrl[option] = decodeURIComponent(value); + break; + default: + anError('An invalid configuration parameter was specified: ' + option); + return; + } + } + + return configFromUrl; +} + +function isaacSetDefault(obj, field, defaultValue) { + if (obj[field] == undefined) { + obj[field] = defaultValue; + return defaultValue; + } + return obj[field]; +} + +function isaacStorageGetRoot() { + var storageItemText = window.localStorage.getItem('annotations') || "{}"; + return JSON.parse(storageItemText); +} + +function isaacStorageSetRoot(obj) { + window.localStorage.setItem('annotations', JSON.stringify(obj)); +} + +function isaacGetFieldPath(obj, fieldPath) { + for (fieldElt of fieldPath) { + obj = obj[fieldElt]; + if (obj == undefined) { + return undefined; + } + } + return obj; +} + +function isaacStorageGet(fieldPath) { + var storageItem = isaacStorageGetRoot(); + return isaacGetFieldPath(storageItem, fieldPath); +} + +function isaacStorageSet(fieldPath, value) { + if (fieldPath.length == 0) { + isaacStorageSetRoot(value); + return; + } + + var storageItem = isaacStorageGetRoot(); + + var obj = storageItem; + for (fieldElt of fieldPath.slice(0, -1)) { + obj = isaacSetDefault(obj, fieldElt, {}); + } + obj[fieldPath[fieldPath.length - 1]] = value; + + isaacStorageSetRoot(storageItem); +} + +function isaacStorageSetDefault(fieldPath, defaultValue) { + var storageItem = isaacStorageGetRoot(); + + var obj = storageItem; + for (fieldElt of fieldPath.slice(0, -1)) { + obj = isaacSetDefault(obj, fieldElt, {}); + } + + var fieldLast = fieldPath[fieldPath.length - 1]; + if (obj[fieldLast] == undefined) { + obj[fieldLast] = defaultValue; + isaacStorageSetRoot(storageItem); + return defaultValue; + } + return obj[fieldLast]; +} + +function isaacStorageDelete(fieldPath) { + var storageItem = isaacStorageGetRoot(); + + var obj = storageItem; + for (fieldElt of fieldPath.slice(0, -1)) { + obj = isaacSetDefault(obj, fieldElt, {}); + } + delete obj[fieldPath[fieldPath.length - 1]]; + + isaacStorageSetRoot(storageItem); +} + +function isaacSaveData(data, fileName) { + var json = JSON.stringify(data, null, 4); + var blob = new Blob([json], {type: 'application/json'}); + var url = window.URL.createObjectURL(blob); + + var a = document.createElement("a"); + a.href = url; + a.download = fileName; + a.style = "display: none"; + document.body.appendChild(a); + + a.click(); + + window.URL.revokeObjectURL(url); + a.remove(); +} + +var ISAAC_HISTORY_MAX_LENGTH = 10; + +function isaacHistoryUpdateEnabled(history) { + document.getElementById('isaac-undo').disabled = (history.undoStack.length == 0); + document.getElementById('isaac-redo').disabled = (history.redoStack.length == 0); +} + +function isaacHistoryDebug(history) { + console.log("#undo=" + history.undoStack.length + " #redo=" + history.redoStack.length) +} + +function isaacHistorySaveState(history, state) { + if (history.current != null) { + history.undoStack.push(history.current); + } + if (history.undoStack.length > ISAAC_HISTORY_MAX_LENGTH) { + history.undoStack.shift(); + } + history.current = state; + history.redoStack = []; + + isaacHistoryUpdateEnabled(history); + isaacHistoryDebug(history); +} + +function isaacRenderState(history, state, imageStoragePath, anno) { + var imageAnnotations = isaacGetFieldPath(state, imageStoragePath) || []; + anno.setAnnotations(imageAnnotations); + isaacHistoryUpdateEnabled(history); +} + +function isaacHistoryUndo(history, imageStoragePath, anno) { + if (history.undoStack.length == 0) { + console.log('got undo request with no undo stack, should never happen'); + return; + } + history.redoStack.push(history.current); + history.current = history.undoStack.pop(); + + isaacStorageSetRoot(history.current); + + isaacRenderState(history, history.current, imageStoragePath, anno); + isaacHistoryDebug(history); +} + +function isaacHistoryRedo(history, imageStoragePath, anno) { + if (history.redoStack.length == 0) { + console.log('got redo request with no redo stack, should never happen'); + return; + } + history.undoStack.push(history.current); + history.current = history.redoStack.pop(); + + isaacStorageSetRoot(history.current); + + isaacRenderState(history, history.current, imageStoragePath, anno); + isaacHistoryDebug(history); +} + +function isaacUpdateAnnotations(fieldPath, value, history) { + isaacStorageSet(fieldPath, value); + isaacHistorySaveState(history, isaacStorageGetRoot()); +} + +function isaacReadAsText(file) { + // Always return a Promise + return new Promise((resolve, reject) => { + let content = ''; + const reader = new FileReader(); + // Wait till complete + reader.onloadend = function(e) { + resolve(e.target.result); + }; + // Make sure to handle error states + reader.onerror = function(e) { + reject(e); + }; + reader.readAsText(file); + }); +} + +function initIsaacSourceImage() { + var configFromUrl = parseUrlParameters(); + + // Initialize OpenSeaDragon viewer and Annotorious plugin + var viewer = OpenSeadragon({ + id: 'container', + prefixUrl: '../../media/openseadragon/', + tileSources: '../../source_images/' + configFromUrl['scene'] + '/' + + configFromUrl['imageId'] + '.dzi' + }); + var anno = OpenSeadragon.Annotorious(viewer); + var history = { + 'current': null, + 'undoStack': [], + 'redoStack': [] + }; + + // Export symbols for debugging + window.configFromUrl = configFromUrl; + window.viewer = viewer; + window.anno = anno; + window.isaacHistory = history; + + // Configure extra point drawing tool (default tools are rect and polygon only) + Annotorious.SelectorPack(anno, { + tools: ['point'] + }); + + // Create toolbar + Annotorious.Toolbar(anno, document.getElementById('isaac-toolbar-container')); + + // Configure other button handlers + var imageStoragePath = [configFromUrl.scene, configFromUrl.imageId]; + document.getElementById('isaac-undo').addEventListener( + "click", + event => isaacHistoryUndo(history, imageStoragePath, anno) + ); + document.getElementById('isaac-redo').addEventListener( + "click", + event => isaacHistoryRedo(history, imageStoragePath, anno) + ); + + var isaacLoadInput = document.getElementById('isaac-load-input'); + document.getElementById('isaac-load').addEventListener('click', function(event) { + isaacLoadInput.click(); + }); + isaacLoadInput.addEventListener('change', async function(event) { + console.log('load change event'); + console.log(isaacLoadInput); + if (isaacLoadInput.files.length > 0) { + var loadFile = isaacLoadInput.files[0]; + var loadText = await isaacReadAsText(loadFile); + var storageItem = JSON.parse(loadText); + isaacStorageSetRoot(storageItem); + isaacHistorySaveState(history, storageItem); + isaacRenderState(history, storageItem, imageStoragePath, anno); + } + }); + + document.getElementById('isaac-save').addEventListener('click', function(event) { + isaacSaveData(isaacStorageGetRoot(), 'annotations.json'); + }); + document.getElementById('isaac-clear').addEventListener('click', function(event) { + isaacRenderState(history, {}, imageStoragePath, anno); + isaacUpdateAnnotations([], {}, history); + }); + + var reviewIndex = 0; + var reviewUpdater = function(delta) { + return function(event) { + var annotations = isaacStorageGet(imageStoragePath); + if (!annotations) return; + reviewIndex = (reviewIndex + delta + annotations.length) % annotations.length; + var annoId = annotations[reviewIndex].id; + anno.selectAnnotation(annoId); + anno.panTo(annoId); + } + } + document.getElementById('isaac-previous').addEventListener('click', reviewUpdater(-1)); + document.getElementById('isaac-next').addEventListener('click', reviewUpdater(1)) + + // Restore annotations on this image from HTML5 local storage + var storageItem = isaacStorageGetRoot(); + isaacRenderState(history, storageItem, imageStoragePath, anno); + isaacHistorySaveState(history, storageItem); + + // Save subsequent drawing events to HTML5 local storage + anno.on('createAnnotation', function(annotation) { + isaacUpdateAnnotations(imageStoragePath, anno.getAnnotations(), history); + }); + anno.on('updateAnnotation', function(annotation) { + isaacUpdateAnnotations(imageStoragePath, anno.getAnnotations(), history); + }); + anno.on('deleteAnnotation', function(annotation) { + isaacUpdateAnnotations(imageStoragePath, anno.getAnnotations(), history); + }); +} + +initIsaacSourceImage(); diff --git a/pano/pano_view/templates/index.html b/pano/pano_view/templates/index.html new file mode 100644 index 00000000..f3eb9416 --- /dev/null +++ b/pano/pano_view/templates/index.html @@ -0,0 +1,15 @@ + + +ISAAC Astrobee Panoramas + + + +

ISAAC Astrobee Panoramas

+ +

[Start exploring here]

+ +

Or jump straight to a specific bay:

+ +{{ index }} + + diff --git a/pano/pano_view/templates/isaac_source_image.html b/pano/pano_view/templates/isaac_source_image.html new file mode 100644 index 00000000..024e7a8a --- /dev/null +++ b/pano/pano_view/templates/isaac_source_image.html @@ -0,0 +1,67 @@ + + + + + + ISAAC Source Image + + + + +
+
+ + +
+ + + + + + +
+
+ +
+
+ + + + + + + diff --git a/pano/pano_view/templates/pannellum.htm b/pano/pano_view/templates/pannellum.htm new file mode 100644 index 00000000..45ce62c9 --- /dev/null +++ b/pano/pano_view/templates/pannellum.htm @@ -0,0 +1,34 @@ + + + + + + Pannellum + + + + + +
+
+ +
+
Navigation controls
+
Overview map
+
Scene links
+
Source image links
+
+
+
+
+ +
+ + + + + diff --git a/scripts/build/build_debian.sh b/scripts/build/build_debian.sh index d4607783..5afc1da3 100755 --- a/scripts/build/build_debian.sh +++ b/scripts/build/build_debian.sh @@ -19,6 +19,7 @@ # under the License. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +DIST=$(. /etc/os-release && echo $UBUNTU_CODENAME) if [ -n "$(git status --porcelain)" ]; then echo "You should not build Debians for a dirty source tree!" @@ -26,12 +27,20 @@ if [ -n "$(git status --porcelain)" ]; then exit -1 fi -EXTRA_FLAGS="-b -a armhf" +EXTRA_FLAGS="-b -aarmhf" + +# In some cases we may want to build for amd64 (e.g. astrobee-comms for users) +if [[ $* == *--native* ]]; then + EXTRA_FLAGS="-b" +fi + if [[ $* == *--config* ]]; then EXTRA_FLAGS="-A" fi pushd $DIR/../.. export CMAKE_TOOLCHAIN_FILE=${DIR}/isaac_cross.cmake -DEB_BUILD_OPTIONS="parallel=8" debuild -e ASTROBEE_WS -e ARMHF_CHROOT_DIR -e ARMHF_TOOLCHAIN -e CMAKE_TOOLCHAIN_FILE -e CMAKE_PREFIX_PATH -us -uc $EXTRA_FLAGS +DEBEMAIL="astrobee-fsw@nx.arc.nasa.gov" DEBFULLNAME="ISAAC Astrobee Software" dch -l"+$DIST" -D"$DIST" "Set distribution '$DIST' for local build" +DEB_BUILD_OPTIONS="parallel=8" debuild -e ASTROBEE_WS -e ARMHF_CHROOT_DIR -e ARMHF_TOOLCHAIN -e CMAKE_TOOLCHAIN_FILE -e CMAKE_PREFIX_PATH -e ROS_DISTRO -e ROS_PYTHON_VERSION -us -uc $EXTRA_FLAGS +# git checkout debian/changelog popd > /dev/null diff --git a/scripts/build/isaac_cross.cmake b/scripts/build/isaac_cross.cmake index d96a2087..cffaae04 100644 --- a/scripts/build/isaac_cross.cmake +++ b/scripts/build/isaac_cross.cmake @@ -16,7 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. -# adds support for the ubuntu cross compile toolchain # this one is important SET(CMAKE_SYSTEM_NAME Linux) @@ -33,14 +32,24 @@ else() SET(ARM_CHROOT_DIR $ENV{HOME}/armhf_xenial_chroot) endif() -SET(CMAKE_SYSROOT ${ARM_CHROOT_DIR}) +if (DEFINED ARMHF_ROS_DISTRO) + SET(ARM_ROS_DISTRO ${ARMHF_ROS_DISTRO}) +else() + SET(ARM_ROS_DISTRO kinetic) +endif() +SET(CMAKE_SYSROOT ${ARM_CHROOT_DIR}) SET(USE_CTC ON CACHE INTERNAL "" FORCE) SET(CROSS_PREFIX arm-linux-gnueabihf) # specify the cross compiler -SET(CMAKE_C_COMPILER $ENV{ARMHF_TOOLCHAIN}/bin/${CROSS_PREFIX}-gcc) -SET(CMAKE_CXX_COMPILER $ENV{ARMHF_TOOLCHAIN}/bin/${CROSS_PREFIX}-g++) +if (ARM_ROS_DISTRO STREQUAL "kinetic") + SET(CMAKE_C_COMPILER $ENV{ARMHF_TOOLCHAIN}/bin/${CROSS_PREFIX}-gcc) + SET(CMAKE_CXX_COMPILER $ENV{ARMHF_TOOLCHAIN}/bin/${CROSS_PREFIX}-g++) +else() + SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) + SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +endif() SET(CMAKE_LIBRARY_ARCHITECTURE ${CROSS_PREFIX}) @@ -74,16 +83,16 @@ execute_process(COMMAND catkin locate -s OUTPUT_VARIABLE CATKIN_SRC_PATH OUTPUT_ SET(catkin2_DIR ${CATKIN_SRC_PATH}/cmake) # needed for gflag to compile... -SET( THREADS_PTHREAD_ARG +SET( THREADS_PTHREAD_ARG "PLEASE_FILL_OUT-FAILED_TO_RUN" CACHE STRING "Result from TRY_RUN" FORCE) # needed to compile eigen, forces result that we are not 64 bit -SET( run_res +SET( run_res "1" CACHE STRING "Result from TRY_RUN" FORCE) -SET( run_res__TRYRUN_OUTPUT +SET( run_res__TRYRUN_OUTPUT "" CACHE STRING "Output from TRY_RUN" FORCE) @@ -101,5 +110,5 @@ SET( ENV{NDDSARCH} "armv6vfphLinux3.xgcc4.7.2" ) # -allow-multiple-definition: because GCC ARM sets every reference as strong. # -rpath-link: so ros can find itself in the darkness SET( CMAKE_EXE_LINKER_FLAGS - "-Wl,-rpath-link,${ARM_CHROOT_DIR}/opt/ros/kinetic/lib:${ARM_CHROOT_DIR}/opt/ros/kinetic/lib/${CROSS_PREFIX}:${ARM_CHROOT_DIR}/lib/${CROSS_PREFIX}:${ARM_CHROOT_DIR}/usr/lib/${CROSS_PREFIX}:${ARM_CHROOT_DIR}/usr/lib/${CROSS_PREFIX}/mesa-egl:${ARM_CHROOT_DIR}/usr/lib/${CROSS_PREFIX}/mesa:${CROSS_PREFIX}/opt/rti/ndds/lib/armv6vfphLinux3.xgcc4.7.2" + "-Wl,-rpath-link,${ARM_CHROOT_DIR}/opt/ros/${ARM_ROS_DISTRO}/lib:${ARM_CHROOT_DIR}/opt/ros/${ARM_ROS_DISTRO}/lib/${CROSS_PREFIX}:${ARM_CHROOT_DIR}/lib/${CROSS_PREFIX}:${ARM_CHROOT_DIR}/usr/lib/${CROSS_PREFIX}:${ARM_CHROOT_DIR}/usr/lib/${CROSS_PREFIX}/mesa-egl:${ARM_CHROOT_DIR}/usr/lib/${CROSS_PREFIX}/mesa:${CROSS_PREFIX}/opt/rti/ndds/lib/armv6vfphLinux3.xgcc4.7.2" CACHE STRING "" FORCE ) diff --git a/scripts/configure.sh b/scripts/configure.sh new file mode 100755 index 00000000..aead6c3a --- /dev/null +++ b/scripts/configure.sh @@ -0,0 +1,328 @@ +#!/bin/bash +# +# Copyright (c) 2017, United States Government, as represented by the +# Administrator of the National Aeronautics and Space Administration. +# +# All rights reserved. +# +# The Astrobee platform 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. +# +# Helper to configure the astrobee build in a simple a repeatable way +# +# This script simply invoke cmake to configure the build with some +# reasonable options for either Native Linux (-l) or/and ArmHF (-a) +# +# The build path (-b), install path (-p) and build type (-B) have +# default values that can be overriden with the according flags. +# Build path and install path are created by default under the home +# directory. + +# First identify more or less where we are (absolute or relative ok) +callcmd=$0 +workdir=`pwd` +confdir=`dirname $callcmd` +scriptname=${0##*/} +rootpath=`cd $confdir/..; pwd` + +cpu_type=`uname -m` +os_kernel=`uname -s | tr '[:upper:]' '[:lower:]'` +gcc_major=`gcc -dumpversion | cut -f-2 -d .` +archname="${cpu_type}_${os_kernel}_gcc${gcc_major}" + +# prefix for installation path +install_path="" + +# configure nothing by default +native_build=0 +armhf_build=0 + +# Define our options +use_ctc=" -DUSE_CTC=off" # Use cross compile toolchain for making ARM binaries +use_static_libs="" # Build using static libraries. Will use lots of drive space. +test_coverage="" # Build the code with code coverage options. Not compatible with USE_CTC. +enable_gprof="" # Enable compling with support for profiling wih gprof (the GNU Profiler)." +enable_google_prof="" # Enable support for profiling wih pprof (the Google Profiler). +enable_integration_testing=" -DENABLE_INTEGRATION_TESTING=on" # Build the integration tests if tests are active. + +# Force cache cleaning by default +clean_cache=1 + +# Default debug mode is off +debug_mode=0 + +# Build target +target="install" + +# short help +usage_string="$scriptname [-h] [-l ] [-a ] [-n ]\ + [-p install_path] [-w workspace_path] [-B build_type] [-c ]\ + [-C ] \ + [-k ] [-K ]\ + [-t ] [-T ]\ + [-g ]" +#[-t make_target] + +# options to parse +optstring="hlan:p:w:B:cCkKtTg" + +# Print the script usage +print_usage() +{ + echo $usage_string +} + +# Print the help message (list all the options) +print_help() +{ + echo "scriptname usage:" + echo $usage_string + echo -e "\t-l Generate a Native Linux build" + echo -e "\t-a Generate a cross-compiled ARM build" + echo -e "\t-n set profile name" + echo -e "\t-p install_path specify the installation directory" + echo -e "\t default=${HOME}/cmake_install_platform" + echo -e "\t-w workspace_path specify the workspace directory" + echo -e "\t default=${ASTROBEE_WS}" + echo -e "\t-B build_type specify build type (Debug|Release|RelWithDebInfo)" + echo -e "\t-c delete the cmake cache before for every modules: default on" + echo -e "\t (necessary when re-running buildall with diffent options)" + echo -e "\t-C do not automatically delete the cmake cache when configure is run" + echo -e "\t (need to be specified to avoid cleaning the cache by default)" + echo -e "\t-k use ccache if available to speed up compilation (default)" + echo -e "\t-K do not use ccache and turn on native optimizations" + echo -e "\t-t build with the integration tests, if tests enabled, requires gpu (default)" + echo -e "\t-T build without integration tests, if tests enabled" + # echo -e "\t-t make_target define the make build target" + # echo -e "\t default (when ommited) is 'install'" + echo -e "\t when -t is specified, the configure processs is skipped!" + echo -e "\t-g prints some debug information and exit" + echo + echo "Warning 1: -p and -b, unlike the other flags that are sticky (because" + echo "cmake cache them), need to be re-issued at each invocation of the" + echo "script. Otherwise the default values will be used instead." + echo "Warning 2: when using both -a and -l simultanously, the options" + echo "-b and -p are ignored since they would override the platform specific" + echo "behavior (for example -p will be the same for ArmHF and Linux)." + echo +} + +# Parse the command line arguments +parse_args() +{ + while getopts $optstring opt $@ + do + case $opt in + "h") print_help + exit 0 + ;; + "?") print_usage + exit 1 + ;; + "l") native_build=1 + ;; + "a") armhf_build=1 + ;; + "n") profile=$OPTARG + ;; + "p") install_path=$OPTARG/ + ;; + "w") workspace_path=$OPTARG/ + ;; + "B") build_type=$OPTARG + ;; + "c") clean_cache=1 + ;; + "C") clean_cache=0 + ;; + "k") use_ccache=" -DUSE_CCACHE=on" + ;; + "K") use_ccache=" -DUSE_CCACHE=off" + ;; + "t") enable_integration_testing=" -DENABLE_INTEGRATION_TESTING=on" + ;; + "T") enable_integration_testing=" -DENABLE_INTEGRATION_TESTING=off" + ;; + "g") debug_mode=1 + ;; + *) print_usage + exit 1 + ;; + esac + done +} + +# Return the full canonical path of a file +# Arguments: +# 1 -> path to canonicalize +# Return: +# 0 if the path exist, 1 if the path does not exist +# prints a string with the canonical path + store it in $canonical_path +canonicalize() +{ + freepath=$1 + os_name=`uname -s` + case $os_name in + Linux) + # just use readlink :-) + canonical_path=`readlink -f $freepath` + readl_ret=$? + echo $canonical_path + if [ $readl_ret == 1 ] ; then + return 1 + else + return 0 + fi + ;; + Darwin | SunOS) + # BSD systems do not support readlink :-( + if [ -d $freepath ] ; then + # the argument is a directory + canonical_path=`cd $freepath && pwd -P` + else + if [ -f $freepath ] ; then + # the argument is a file + freedir=`dirname $freepath` + freefile=`basename $freepath` + if [ -L $freepath ] ; then + canfile=`cd $freedir && stat -f "%Y" $freefile` + else + canfile=$freefile + fi + candir=`cd $freedir && pwd -P` + canonical_path="${candir}/${canfile}" + else + # given path does not exsit + # since readlink does not return any string for this + # scenario, just lets do the same and return an error + canonical_path="" + return 1 + fi + fi + echo $canonical_path + return 0 + ;; + *) + # echo platform not supported yet + echo "/${os_name}/is/not/yet/supported/by/canonicalize" + return 1 + ;; + esac +} + +# Start the real work here... +parse_args $@ + +# Define the paths to use +ff_path=`canonicalize ${rootpath}` + +DIST=`cat /etc/os-release | grep -oP "(?<=VERSION_CODENAME=).*"` +if [ "$DIST" = "xenial" ]; then + ros_version=kinetic +elif [ "$DIST" = "bionic" ]; then + ros_version=melodic +elif [ "$DIST" = "focal" ]; then + ros_version=noetic +fi + +if [ $debug_mode == 1 ]; then + echo "script is called from: $workdir" + echo "script dir is: $confdir" + echo "scriptname is: $scriptname" + echo "absolute script path: $rootpath" + echo "freeflyer canonical path: ${ff_path}" + echo "Other Options:" ${extra_opts} + echo "linux build enabled: ${native_build}" + echo "armhf build enabled: ${armhf_build}" + echo +fi + +extra_opts+=${use_ccache}${use_ros}${use_dds}${use_static_libs}${test_coverage}${use_drivers} +extra_opts+=${enable_gprof}${enable_google_prof} +extra_opts+=${enable_integration_testing} + + +if [[ $native_build == 0 && $armhf_build == 0 ]] ; then + echo "Nothing to configure (use -l or -a)..." + echo "Use $scriptname -h for the full list of options" + print_usage +fi + +if [[ $native_build == 1 && $armhf_build == 1 ]] ; then + echo -n "Linux and ArmHF invoked simultanously:" + echo " dropping any option -p and -b!" + workspace_path="" + install_path="" +fi + +if [ $native_build == 1 ] ; then + echo "configuring for native linux..." + catkin init + + # Add our cmake to paths and bashrc + grep -qF 'source /opt/ros/'$ros_version'/setup.bash' ~/.bashrc || echo 'source /opt/ros/'$ros_version'/setup.bash' >> ~/.bashrc + cmake_astrobee_path=`catkin locate -s`/cmake + grep -qF ${cmake_astrobee_path} ~/.bashrc || { + echo -e '\n' >> ~/.bashrc + echo 'if [[ ":$CMAKE_PREFIX_PATH:" != *":'${cmake_astrobee_path}':"* ]]; then CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH:+"$CMAKE_PREFIX_PATH:"}'${cmake_astrobee_path}'"; fi' >> ~/.bashrc + } + source ~/.bashrc + + shell="$SHELL" + if [[ ${shell} == *"zsh"* ]]; then + echo "Setting .zshrc with environment variables..." + grep -qF 'source /opt/ros/'$ros_version'/setup.zsh' ~/.zshrc || echo 'source /opt/ros/'$ros_version'/setup.zsh' >> ~/.zshrc + grep -qF ${cmake_astrobee_path} ~/.zshrc || { + echo -e '\n' >> ~/.zshrc + echo 'if [[ ":$CMAKE_PREFIX_PATH:" != *":'${cmake_astrobee_path}':"* ]]; then CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH:+"$CMAKE_PREFIX_PATH:"}'${cmake_astrobee_path}'"; fi' >> ~/.zshrc + } + fi + + catkin profile add ${profile:-native} + catkin profile set ${profile:-native} + catkin config --no-extend \ + --build-space ${workspace_path}build \ + --install-space ${install_path:-${workspace_path}install} \ + --devel-space ${workspace_path}devel \ + --log-space ${workspace_path}log \ + --no-install \ + --cmake-args ${extra_opts} + + +fi + +if [ $armhf_build == 1 ] ; then + echo "configuring for armhf..." + catkin init + armhf_opts="-DCMAKE_TOOLCHAIN_FILE=${ff_path}/scripts/build/isaac_cross.cmake -DARMHF_ROS_DISTRO=${ros_version} -DCATKIN_ENABLE_TESTING=off" + use_ctc=" -DUSE_CTC=on" + enable_gazebo="" + build_loc_rviz_plugins="" + catkin profile add ${profile:-armhf} + catkin profile set ${profile:-armhf} + catkin config --extend $ASTROBEE_WS/armhf/opt/astrobee \ + --build-space ${workspace_path:-armhf/}build \ + --install-space ${install_path:-${workspace_path:-armhf/}}opt/isaac \ + --devel-space ${workspace_path:-armhf/}devel \ + --log-space ${workspace_path:-armhf/}logs \ + --install \ + --cmake-args -DARMHF_CHROOT_DIR=$ARMHF_CHROOT_DIR ${armhf_opts} ${use_ctc} ${extra_opts} \ + -DCMAKE_BUILD_TYPE=Release + if [ "$DIST" = "xenial" ] || [ "$DIST" = "bionic" ]; then + catkin config \ + --whitelist isaac_astrobee_description isaac_util isaac_msgs inspection cargo isaac_hw_msgs wifi isaac gs_action_helper + elif [ "$DIST" = "focal" ]; then + catkin config \ + --buildlist isaac_astrobee_description isaac_util isaac_msgs inspection cargo isaac_hw_msgs wifi isaac gs_action_helper + fi + +fi diff --git a/scripts/docker/analyst.Dockerfile b/scripts/docker/analyst.Dockerfile index 2a878840..bedae889 100644 --- a/scripts/docker/analyst.Dockerfile +++ b/scripts/docker/analyst.Dockerfile @@ -24,12 +24,16 @@ ARG REMOTE=isaac FROM ${REMOTE}/isaac:msgs-ubuntu20.04 RUN apt-get update \ - && apt-get install -y python3-pip \ + && apt-get install -y \ + python3-pip \ + ffmpeg libsm6 libxext6 \ + ros-noetic-cv-bridge \ && rm -rf /var/lib/apt/lists/* RUN pip3 install pyArango \ - && pip3 install torch==1.10.2+cpu torchvision==0.11.3+cpu -f https://download.pytorch.org/whl/cpu/torch_stable.html \ - && pip3 install jupyterlab jupyterhub nbconvert Pygments==2.6.1 + && pip3 install jupyterlab jupyterhub nbconvert Pygments==2.6.1 jupyros \ + && pip3 install matplotlib opencv-python \ + && pip3 install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu EXPOSE 8888 diff --git a/scripts/docker/build.sh b/scripts/docker/build.sh index 80542ca9..d4b686e5 100755 --- a/scripts/docker/build.sh +++ b/scripts/docker/build.sh @@ -53,6 +53,7 @@ print_usage() os=`cat /etc/os-release | grep -oP "(?<=VERSION_CODENAME=).*"` mast=1 vm=0 +REMOTE=isaac while [ "$1" != "" ]; do case $1 in @@ -73,6 +74,8 @@ while [ "$1" != "" ]; do ;; -n | --no-mast ) mast=0 ;; + -r | --remote ) REMOTE=ghcr.io/nasa + ;; -v | --vm ) vm=1 ;; -h | --help ) print_help @@ -91,7 +94,7 @@ rootdir=$(realpath ${thisdir}/../..) # Define root dir of different repos astrobee_source=$(realpath ${astrobee_source:-${rootdir}/../../astrobee/src}) isaac_source=${rootdir} -idi_source=$(realpath ${idi_source:-${rootdir}/../../isaac_data_interface}) +idi_source=$(realpath ${idi_source:-${rootdir}/../../isaac_user_interface}) if $mast; then mast_source=$(realpath ${mast_source:-${rootdir}/../../mast/src}) fi @@ -160,6 +163,13 @@ docker build ${isaac_source:-${rootdir}} \ --build-arg ROS_VERSION=${ROS_VERSION} \ --build-arg PYTHON=${PYTHON} \ -t isaac/isaac:msgs-ubuntu${UBUNTU_VERSION} +# Build analyst +docker build ${isaac_source:-${rootdir}} \ + -f ${isaac_source:-${rootdir}}/scripts/docker/analyst.Dockerfile \ + --build-arg UBUNTU_VERSION=${UBUNTU_VERSION} \ + --build-arg ROS_VERSION=${ROS_VERSION} \ + --build-arg PYTHON=${PYTHON} \ + -t isaac_analyst_notebook:latest # Build IDI and MAST export IDI_PATH=${idi_source} @@ -167,9 +177,12 @@ export MAST_PATH=${mast_source} export UBUNTU_VERSION=${UBUNTU_VERSION} export ROS_VERSION=${ROS_VERSION} export PYTHON=${PYTHON} +export REMOTE=${REMOTE} +cd ${IDI_PATH} +pwd if [ $mast == 1 ]; then - docker-compose -f ${thisdir}/docker_compose/ros.docker-compose.yml -f ${IDI_PATH}/idi.docker-compose.yml -f ${thisdir}/docker_compose/mast.docker_compose.yml build + docker-compose -f docker-compose.yml -f plugins/ros.docker-compose.yml -f plugins/isaac.docker-compose.yml -f ${thisdir}/docker_compose/mast.docker_compose.yml build else - docker-compose -f ${thisdir}/docker_compose/ros.docker-compose.yml -f ${IDI_PATH}/idi.docker-compose.yml build + docker-compose -f docker-compose.yml -f plugins/ros.docker-compose.yml -f plugins/isaac.docker-compose.yml build fi diff --git a/scripts/docker/build_apk.Dockerfile b/scripts/docker/build_apk.Dockerfile new file mode 100644 index 00000000..5556e0d1 --- /dev/null +++ b/scripts/docker/build_apk.Dockerfile @@ -0,0 +1,68 @@ +# Copyright (c) 2021, 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. + +# 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 REMOTE=isaac +FROM ${REMOTE}/isaac:msgs-ubuntu16.04 + +RUN apt-get update && apt-get install -y \ + unzip \ + libc6-dev-i386 \ + lib32z1 \ + python-wstool \ + openjdk-8-jdk \ + ros-kinetic-rosjava \ + && rm -rf /var/lib/apt/lists/* + +# Compile msg jar files, genjava_message_artifacts only works with bash +RUN ["/bin/bash", "-c", "cd /src/msgs \ + && catkin config \ + && catkin build \ + && . devel/setup.bash \ + && genjava_message_artifacts --verbose -p ff_msgs ff_hw_msgs isaac_msgs isaac_hw_msgs"] + +# Copy over the isaac apks +COPY apks /src/isaac/apks + +# Replace compiles messages with new ones +RUN rm /src/isaac/apks/isaac_gs_ros_bridge/app/libs/*msgs* \ + && cd /src/msgs/devel/share/maven \ + && find . -name *.jar -print0 | xargs -0 cp -t /src/isaac/apks/isaac_gs_ros_bridge/app/libs + +# Install APK dependencies +RUN sudo apt-get update \ + && apt-get install -y libc6-dev-i386 lib32z1 openjdk-8-jdk \ + && mkdir $HOME/android-sdk \ + && cd $HOME/android-sdk \ + && wget https://dl.google.com/android/repository/tools_r25.2.3-linux.zip \ + && java -version \ + && unzip tools_r25.2.3-linux.zip \ + && tools/bin/sdkmanager --update \ + && yes | tools/bin/sdkmanager "platforms;android-25" "build-tools;25.0.2" "extras;google;m2repository" "extras;android;m2repository" \ + && wget https://dl.google.com/android/repository/android-ndk-r22b-linux-x86_64.zip \ + && unzip android-ndk-r22b-linux-x86_64.zip \ + && mv android-ndk-r22b ndk-bundle \ + && cd ~/android-sdk/ndk-bundle/toolchains \ + && ln -s aarch64-linux-android-4.9 mips64el-linux-android \ + && ln -s arm-linux-androideabi-4.9 mipsel-linux-android + +# Build APK +RUN cd /src/isaac/apks/isaac_gs_ros_bridge \ + && ANDROID_HOME=$HOME/android-sdk ANDROID_NDK_HOME=$HOME/android-sdk/ndk-bundle ./gradlew build \ No newline at end of file diff --git a/scripts/docker/docker_compose/analyst.docker-compose.yml b/scripts/docker/docker_compose/analyst.docker-compose.yml index 702e3993..da79d908 100644 --- a/scripts/docker/docker_compose/analyst.docker-compose.yml +++ b/scripts/docker/docker_compose/analyst.docker-compose.yml @@ -6,8 +6,9 @@ services: # Launches analyst notebook # image: isaac_analyst_notebook + command: "/ros_entrypoint.sh" hostname: isaac_notebook - container_name: isaac_notebook + container_name: ${REMOTE}isaac_notebook build: context: '..' dockerfile: 'analyst.Dockerfile' @@ -15,13 +16,14 @@ services: REMOTE: ${REMOTE:-isaac} ports: - "8888:8888" - networks: - - isaac environment: - JUPYTER_TOKEN=isaac - ROS_MASTER_URI=http://rosmaster:11311 + - ROS_IP=172.19.0.12 + networks: + isaac: + ipv4_address: 172.19.0.12 volumes: - ${ISAAC_PATH:-.}/analyst/workspace:/home/analyst/ - ${DATA_PATH}:/home/analyst/data - - ${BAGS_PATH}:/home/analyst/data/bags - command: "/ros_entrypoint.sh" + - ${BAGS_PATH}:/home/analyst/data/bags \ No newline at end of file diff --git a/scripts/docker/docker_compose/astrobee.docker-compose.display.yml b/scripts/docker/docker_compose/astrobee.docker-compose.display.yml new file mode 100644 index 00000000..712b682c --- /dev/null +++ b/scripts/docker/docker_compose/astrobee.docker-compose.display.yml @@ -0,0 +1,9 @@ +version: "3.9" +services: + isaac: + environment: + - XAUTHORITY=$XAUTH + - DISPLAY=$DISPLAY + volumes: + - $XSOCK:$XSOCK + - $XAUTH:$XAUTH diff --git a/scripts/docker/docker_compose/astrobee.docker-compose.gpu.yml b/scripts/docker/docker_compose/astrobee.docker-compose.gpu.yml new file mode 100644 index 00000000..87d62b80 --- /dev/null +++ b/scripts/docker/docker_compose/astrobee.docker-compose.gpu.yml @@ -0,0 +1,8 @@ +version: "3.9" +services: + isaac: + deploy: + resources: + reservations: + devices: + - capabilities: [gpu] diff --git a/scripts/docker/docker_compose/astrobee.docker-compose.mount.yml b/scripts/docker/docker_compose/astrobee.docker-compose.mount.yml new file mode 100644 index 00000000..24baab51 --- /dev/null +++ b/scripts/docker/docker_compose/astrobee.docker-compose.mount.yml @@ -0,0 +1,13 @@ +version: "3.9" +services: + isaac: + volumes: + - type: bind + source: ${ASTROBEE_PATH} + target: /src/astrobee/src + - type: bind + source: ${ISAAC_PATH} + target: /src/isaac/src + + + diff --git a/scripts/docker/docker_compose/astrobee.docker-compose.yml b/scripts/docker/docker_compose/astrobee.docker-compose.yml index 43afb393..0ef6c4fc 100644 --- a/scripts/docker/docker_compose/astrobee.docker-compose.yml +++ b/scripts/docker/docker_compose/astrobee.docker-compose.yml @@ -1,11 +1,11 @@ -version: "2.1" +version: "3.9" services: - astrobee: - image: isaac:latest + isaac: + image: ${REMOTE}$tag entrypoint: "/ros_entrypoint.sh" - command: roslaunch isaac sim.launch gzserver:=false glp:=disabled dds:=false r2:=true - hostname: astrobee - container_name: astrobee + command: roslaunch isaac sim.launch dds:=false robot:=sim_pub + hostname: isaac + container_name: isaac expose: - 11311 environment: @@ -15,8 +15,3 @@ services: networks: isaac: ipv4_address: 172.19.0.6 - -networks: - isaac: - external: - name: isaac \ No newline at end of file diff --git a/scripts/docker/docker_compose/idi.docker-compose.yml b/scripts/docker/docker_compose/iui.docker-compose.yml similarity index 93% rename from scripts/docker/docker_compose/idi.docker-compose.yml rename to scripts/docker/docker_compose/iui.docker-compose.yml index c2ede22e..d7f614c9 100644 --- a/scripts/docker/docker_compose/idi.docker-compose.yml +++ b/scripts/docker/docker_compose/iui.docker-compose.yml @@ -37,7 +37,7 @@ # # docker-compose down -v # -version: "2.1" +version: "3.9" services: # -------------------------------------------------------------------------------------------------- # Essential IDI Services @@ -46,7 +46,7 @@ services: # This service serves the static content that represents # the frontend ISAAC data interface. # - image: ${REMOTE}/isaac_user_interface_frontend + image: ${REMOTE}iui_frontend hostname: iui_frontend container_name: iui_frontend ports: @@ -65,7 +65,7 @@ services: # This service serves the front-end by providing it with # historical telemetry that it retrieves from ROS bridge. # - image: ${REMOTE}/isaac_user_interface_backend + image: ${REMOTE}iui_backend hostname: iui_backend container_name: iui_backend command: python /main.py @@ -106,11 +106,11 @@ services: # For more info, see: # http://wiki.ros.org/rosbridge_suite # - image: ${REMOTE}/isaac_user_interface_rosbridge + image: ${REMOTE}iui_rosbridge hostname: rosbridge container_name: rosbridge - entrypoint: "/ros_entrypoint.sh" - command: "roslaunch --wait /rosbridge/publishers.launch" + entrypoint: '/ros_entrypoint.sh' + command: 'roslaunch --wait /rosbridge/publishers.launch' ports: - "9090:9090" depends_on: diff --git a/scripts/docker/docker_compose/ros.docker-compose.yml b/scripts/docker/docker_compose/ros.docker-compose.yml index d726fa32..32aa546a 100644 --- a/scripts/docker/docker_compose/ros.docker-compose.yml +++ b/scripts/docker/docker_compose/ros.docker-compose.yml @@ -1,4 +1,4 @@ -version: "2.1" +version: "3.9" services: rosmaster: image: ros:kinetic diff --git a/scripts/docker/isaac_astrobee.Dockerfile b/scripts/docker/isaac_astrobee.Dockerfile index a6d2b147..269c85ca 100644 --- a/scripts/docker/isaac_astrobee.Dockerfile +++ b/scripts/docker/isaac_astrobee.Dockerfile @@ -37,7 +37,7 @@ RUN apt-get update && apt-get install -y \ COPY astrobee /src/isaac/src/astrobee/ COPY isaac /src/isaac/src/isaac/ COPY description /src/isaac/src/description/ -COPY isaac_msgs /src/isaac/src/isaac_msgs/ +COPY communications/isaac_msgs /src/isaac/src/communications/isaac_msgs/ COPY shared /src/isaac/src/shared/ RUN . /src/astrobee/devel/setup.sh \ diff --git a/scripts/docker/isaac_msgs.Dockerfile b/scripts/docker/isaac_msgs.Dockerfile index e06c7232..27190252 100644 --- a/scripts/docker/isaac_msgs.Dockerfile +++ b/scripts/docker/isaac_msgs.Dockerfile @@ -27,7 +27,7 @@ ARG ROS_VERSION=kinetic ARG PYTHON="" # Copy over the isaac_msgs -COPY isaac_msgs /src/msgs/src/ +COPY communications/isaac_msgs /src/msgs/src/communications/ RUN cd /src/msgs \ && export CMAKE_PREFIX_PATH=/opt/ros/${ROS_VERSION} \ diff --git a/scripts/docker/readme.md b/scripts/docker/readme.md index 57223929..9577faf1 100644 --- a/scripts/docker/readme.md +++ b/scripts/docker/readme.md @@ -5,36 +5,27 @@ Install dependencies Install docker tools: https://docs.docker.com/engine/install/ubuntu/ -Install docker compose: https://docs.docker.com/compose/install/ - -Install nvidia-docker: https://github.com/NVIDIA/nvidia-docker. +Install nvidia-docker (optional, to use GPU): https://github.com/NVIDIA/nvidia-docker. Building the docker images --------- -To run the demos, you can use the remote images and skip this section. -If you want to build the docker images locally instead of pulling from the remote repository, run: +To run the demos, you can use the remote pre-built images hosted on Github and skip this section. +If you want to build the docker images locally instead of pulling from the remote repository, use: ./build.sh [OPTIONS] -The build script will automatically detect the current Ubuntu OS version and define the docker files variables -`UBUNTU_VERSION`, `ROS_VERSION`, and `PYTHON` accordingly. +**Before** running this script, please check the available options and defaults with: -Options: + ./build --help -If a specific version is desired, the option --xenial, --bionic, +The build script will automatically detect the current Ubuntu OS version and define the docker files variables +`UBUNTU_VERSION`, `ROS_VERSION`, and `PYTHON` accordingly. If a specific version is desired, the option --xenial, --bionic, and --focal is used for ubuntu 16.04, 18.04, and 20.04 docker images, respectively. -If you are building the docker images in a virtual machine, because of graphics card passthrough -restriction, the gazebo simulation will not work properly. Therefore the build script has the option --vm -which will only build the images that are used for a virtual machine deployment (simulation runs natively). - If you don't want to run mast or don't have access to it (not a public repository), the use the option --no-mast. -For further help on available options: - - ./build --help Run the docker containers --------- @@ -43,51 +34,55 @@ To run the docker containers: ./run.sh [OPTIONS] -It will automatically detect the current Ubuntu OS version. If a specific version is desired, the option ---xenial, --bionic, and --focal is used for ubuntu 16.04, 18.04, and 20.04 docker images, respectively. - -Options: +**Before** running this script, please check the available options and defaults with: -If a specific version is desired, the option --xenial, --bionic, -and --focal is used for ubuntu 16.04, 18.04, and 20.04 docker images, respectively. -Note this is only valid for the docker containers, native software will run on the host OS. + ./run --help +Make sure the default paths are correct, if not configure those options. Read through the +different optional modules to understand if it fits your purpose. +It will automatically detect the current Ubuntu OS version. If a specific version is desired, the option +--xenial, --bionic, and --focal is used for ubuntu 16.04, 18.04, and 20.04 docker images, respectively. +Once the command is executed the host location of the modules launched will be printed. Open those paths +on your favorite browser. +Shutdown docker containers +--------- -For further help on available options: +To stop all of the containers, use: - ./run --help + scripts/docker/shutdown.sh +Docker Demos +---------- +There are currently 3 demos available to showcase some aspects of the ISAAC functionality. +Open `http://127.0.0.1:8080` in a web browser to see what is happening. Use +`docker ps` to see the docker containers and use `docker exec -it container_name /bin/bash` to get a shell in one. -http://localhost:8888/lab?token=isaac +Cancel with Ctrl+c and then run `scripts/docker/shutdown.sh` to stop the demo. -Docker Demos ----------- -There are currently 3 demos available to showcase some aspects of the ISAAC functionality +### Trigger anomaly (with MAST Only) -### Trigger anomaly + ./demos/trigger_anomaly.sh This demo will trigger a C02 anomaly, the levels of C02 will start to increase. The mast detects the anomaly and sends astrobee to inspect a vent. Astrobee will undock, calculate the optimal inspection pose to observe the target and move towards that pose, replanning if any obstacle is found. When the robot has the vent of interest in sight, it will take a picture and run it through a trained CNN, identifying whether the vent is obstructed, free or inconclusive result. After inspection Astrobee will dock autonomously. ### Trigger geometric mapping + ./demos/trigger_geometric_mapping.sh + This demo will trigger a geometric mapping inspection event. The geometric mapper collects pictures from several poses and creates a 3d mesh of the ISS. -The robot will undock, follow a trajectory taking pictures at the specified waypoints and dock again. For the geometric mapper, the trajectory followed is defined in astrobee/behaviors/inspection/resources/jpm_sliced.txt. The geometric mapper will map a section of the jem containing the entry node. +The robot will undock, follow a trajectory taking pictures at the specified waypoints and dock again. For the geometric mapper, the trajectory followed is defined in astrobee/behaviors/inspection/resources/geometry_iss.txt. The geometric mapper will map a section of the jem containing bay 5. ### Trigger wifi mapping -This demo will trigger a volumetric mapping inspection event. The volumetric mapper collects information from an onboard sensor of Astrobee and interpolates the data in a specified area. -The robot will undock, follow a trajectory and dock again. For the wifi mapper, the trajectory followed is defined in astrobee/behaviors/inspection/resources/wifi.txt. - + ./demos/trigger_wifi_mapping.sh -Open `http://127.0.0.1:8080` in a web browser to see what is happening. Use -`docker ps` to see the docker containers and use `docker exec -it container_name /bin/bash` to get a shell in one. - -Cancel with Ctrl+c and then run `scripts/docker/shutdown.sh` to stop the demo. +This demo will trigger a volumetric mapping inspection event. The volumetric mapper collects information from an onboard sensor of Astrobee and interpolates the data in a specified area. +The robot will undock, follow a trajectory and dock again. For the wifi mapper, the trajectory followed is defined in astrobee/behaviors/inspection/resources/volumetric_iss.txt. diff --git a/scripts/docker/run.sh b/scripts/docker/run.sh index 4bd8bfb1..734a5911 100755 --- a/scripts/docker/run.sh +++ b/scripts/docker/run.sh @@ -34,11 +34,11 @@ print_help() echo -e "\t\t\t\tdefault=isaac_source/../../isaac_data_interface" echo -e "\t-m | --mast-source-dir\t\tSpecify the mast source directory to use" echo -e "\t\t\t\tdefault=isaac_source/../../mast/src" - echo -e "\t--no-mast\t\t\tDon't run MAST" + echo -e "\t--mast\t\t\tRun MAST" echo -e "\t-vm\t\t\t\tRun the simulation locally - compatible with VM setup" echo -e "\t-remote\t\t\t\tRun the remote images (bypass building docker images locally)" echo -e "\t-analyst\t\t\tRun analyst notebook" - echo -e "\t--no-simvm\t\t\tDon't run the simulation" + echo -e "\t--no-sim\t\t\tDon't run the simulation" echo -e "\t-g | --ground-only\t\tRun only the isaac ground software (hardware in the loop only)" echo -e "\t-robot\t\t\t\tSpecify robot name (hardware in the loop only)" echo @@ -53,35 +53,54 @@ print_usage() # Initialize variables os=`cat /etc/os-release | grep -oP "(?<=VERSION_CODENAME=).*"` -mast=1 # MAST is ON by default -vm=0 # We are not running in a virtual machine by default -remote=0 -ground=0 -analyst= +sim_args="dds:=false robot:=sim_pub wifi:=station" +display="true" +vendor_name=`(lshw -C display | grep vendor) 2>/dev/null` +if [ "$vendor_name" == *"Nvidia"* ]; then + gpu="true" +else + gpu="false" +fi +mast=0 # MAST is OFF by default +ground=0 # Ground SW HIL OFF by default +analyst=0 +mount="false" robot=bumble no_sim=0 +REMOTE="isaac" while [ "$1" != "" ]; do case $1 in + -h | --help ) print_help + exit + ;; -x | --xenial ) os="xenial" ;; -b | --bionic ) os="bionic" ;; -f | --focal ) os="focal" ;; + -r | --remote ) export REMOTE="ghcr.io/nasa/" + ;; + -a | --astrobee-source-dir ) shift + astrobee_source=$1 + ;; -d | --iui-source-dir ) shift iui_source=$1 ;; -m | --mast-source-dir ) shift mast_source=$1 ;; - --no-mast ) mast=0 + --mast ) mast=1 + ;; + -analyst ) analyst=1 ;; - -vm ) vm=1 + -n | --no-display ) display="false" ;; - -remote ) remote=1 + -g | --gpu) gpu="$2" + shift ;; - -analyst ) analyst=1 + --mount ) mount="true" ;; --no-sim ) no_sim=1 ;; @@ -90,8 +109,8 @@ while [ "$1" != "" ]; do -robot ) shift robot=$1 ;; - -h | --help ) print_help - exit + --args ) sim_args+=" $2" + shift ;; * ) print_usage exit 1 @@ -99,99 +118,132 @@ while [ "$1" != "" ]; do shift done -thisdir=$(dirname "$(readlink -f "$0")") -rootdir=${thisdir}/../.. - -# Define root dir of different repos -isaac_source=${rootdir}/./ -iui_source=${iui_source:-${rootdir}/../../isaac_user_interface} -mast_source=${mast_source:-${rootdir}/../../mast/src} -robot=${robot:-bumble} - -export ISAAC_PATH=${isaac_source} -export IUI_PATH=${iui_source} -export MAST_PATH=${mast_source} - -# Define data locations for analyst notebook -export DATA_PATH=${HOME}/data -export BAGS_PATH=$(readlink -f ${HOME}/data/bags) - -echo "ISAAC UI path: "${iui_source} -echo "Build MAST?:" $mast " MAST path: "${mast_source} +# collect remaining non-option arguments +cmd="$*" +if [ -z "$cmd" ]; then + cmd="roslaunch isaac sim.launch $sim_args" +fi -UBUNTU_VERSION=16.04 -ROS_VERSION=kinetic -PYTHON='' +# echo "cmd: $cmd" -if [ "$os" = "bionic" ]; then - UBUNTU_VERSION=18.04 - ROS_VERSION=melodic - PYTHON='' +###################################################################### +# Resolve paths +###################################################################### +script_dir=$(dirname "$(readlink -f "$0")") +src_dir=$(dirname $(dirname "$script_dir")) +# Define root dir of different repos +export ASTROBEE_PATH=${astrobee_source:-$(dirname $(dirname "$src_dir"))/astrobee/src} +export ISAAC_PATH=${src_dir} +export IUI_PATH=${iui_source:-$(dirname $(dirname "$src_dir"))/isaac_user_interface} +export MAST_PATH=${mast_source:-$(dirname $(dirname "$src_dir"))/mast/src} + +# Print debug variables +echo -e ====================================================================== +echo -e "astrobee path: \t "${ASTROBEE_PATH} +echo -e "isaac path: \t "${ISAAC_PATH} +echo -e "isaac ui path: \t "${IUI_PATH} +echo -e "mast path: \t "${MAST_PATH} " \t build mast?:" $mast +echo -e ====================================================================== + +###################################################################### +# Set up tag +###################################################################### + +if [ "$os" = "xenial" ]; then + export tag=isaac:latest-ubuntu16.04 +elif [ "$os" = "bionic" ]; then + export tag=isaac:latest-ubuntu18.04 elif [ "$os" = "focal" ]; then - UBUNTU_VERSION=20.04 - ROS_VERSION=noetic - PYTHON='3' + export tag=isaac:latest-ubuntu20.04 fi -if [ $remote -eq 1 ]; then - export REMOTE=ghcr.io/nasa -fi +###################################################################### +# Set up docker compose +###################################################################### # Launch the rosmaster container + isaac network + IDI -files="-f ${thisdir}/docker_compose/ros.docker-compose.yml -f ${thisdir}/docker_compose/idi.docker-compose.yml" -echo -e "The ISAAC UI is hosted in: http://localhost:8080" -echo -e "The ArangoDB database is hosted in: http://localhost:8529" +files="-f ${script_dir}/docker_compose/ros.docker-compose.yml -f ${script_dir}/docker_compose/iui.docker-compose.yml -f ${script_dir}/docker_compose/astrobee.docker-compose.yml" +echo -e "isaac ui hosted in: \t http://localhost:8080" +echo -e "database hosted in: \t http://localhost:8529" # Launch MAST +###################################################################### if [ $mast -eq 1 ]; then - files+=" -f ${thisdir}/docker_compose/mast.docker_compose.yml" + files+=" -f ${script_dir}/docker_compose/mast.docker_compose.yml" fi # Launch the analyst notebook +###################################################################### if [ $analyst -eq 1 ]; then - files+=" -f ${thisdir}/docker_compose/analyst.docker-compose.yml" - echo -e "The Analyst Notebook is hosted in: http://localhost:8888/lab?token=isaac" + # Define data locations for analyst notebook + export DATA_PATH=${HOME}/data + export BAGS_PATH=$(readlink -f ${HOME}/data/bags) + + files+=" -f ${script_dir}/docker_compose/analyst.docker-compose.yml" + echo -e "analyst notebook hosted in: \t http://localhost:8888/lab?token=isaac" fi -docker-compose ${files} up -d +# Set up astrobee display +###################################################################### + +display_args="" +if [ "$display" = "true" ]; then + # setup XServer for Docker + export XSOCK=/tmp/.X11-unix + export XAUTH=/tmp/.docker.xauth + touch $XAUTH + xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge - + + files+=" -f ${script_dir}/docker_compose/astrobee.docker-compose.display.yml" + + if [ "$gpu" = "true" ]; then + files+=" -f ${script_dir}/docker_compose/astrobee.docker-compose.gpu.yml" + fi +fi + +# Set up astrobee mount +###################################################################### + +mount_args="" +if [ "$mount" = "true" ]; then + files+=" -f ${script_dir}/docker_compose/astrobee.docker-compose.mount.yml" +fi if [ $ground -eq 1 ]; then - echo "GROUND" - # Start native astrobee simulation and connect to socket network - export ROS_IP=`ip -4 addr show docker0 | grep -oP "(?<=inet ).*(?=/)"` - export ROS_MASTER_URI=http://172.19.0.5:11311 - roslaunch isaac isaac_astrobee.launch llp:=disabled mlp:=disabled ilp:=disabled streaming_mapper:=true output:=screen robot:=${robot} --wait - -elif [ $vm -eq 1 ]; then - echo "VM" - # Start native astrobee simulation and connect to socket network - export ROS_IP=`ip -4 addr show docker0 | grep -oP "(?<=inet ).*(?=/)"` - export ROS_MASTER_URI=http://172.19.0.5:11311 - roslaunch isaac isaac_astrobee.launch llp:=disabled mlp:=disabled ilp:=disabled streaming_mapper:=true output:=screen robot:=${robot} --wait - -elif [ $no_sim -eq 0 ]; then + echo "GROUND SW ONLY (for HIL)" + robot=${robot:-bumble} + cmd=roslaunch isaac isaac_astrobee.launch llp:=disabled mlp:=disabled ilp:=disabled streaming_mapper:=true output:=screen robot:=${robot} --wait + +elif [ $no_sim -eq 1 ]; then echo "NO SIM" - XSOCK=/tmp/.X11-unix - XAUTH=/tmp/.docker.xauth - touch $XAUTH - xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge - - - docker run -d --rm --name isaac \ - --network docker_isaac \ - --volume=$XSOCK:$XSOCK:rw \ - --volume=$XAUTH:$XAUTH:rw \ - --volume=`pwd`/simulation.config:/src/astrobee/astrobee/config/simulation/simulation.config:ro \ - --volume=`pwd`/simulation.config:/opt/astrobee/config/simulation/simulation.config:ro \ - --env="XAUTHORITY=${XAUTH}" \ - --env="DISPLAY" \ - --user="astrobee" \ - --env="ROS_MASTER_URI=http://rosmaster:11311" \ - --gpus all \ - isaac \ - /ros_entrypoint.sh roslaunch isaac sim.launch dds:=false wifi:=station streaming_mapper:=true acoustics_cam:=true + cmd=roslaunch isaac sim.launch llp:=disabled glp:=disabled gzserver:=false nodes:="framestore,isaac_framestore" output:=screen robot:=${robot} rviz:=true --wait fi +# Up compose +###################################################################### + +echo -e ====================================================================== +docker compose ${files} up -d + +echo -e "Started containers, press 'q' to down container and exit" + +# Down compose +###################################################################### + +count=0 +while : ; do +read -n 1 k <&1 +if [[ $k = q ]] ; then +printf "\nQuitting from the program\n" +break +else +((count=$count+1)) +printf "\nIterate for $count times\n" +echo "Press 'q' to exit" +fi +done +docker compose ${files} down \ No newline at end of file diff --git a/scripts/docker/shutdown.sh b/scripts/git/configure_isort_paths.sh similarity index 53% rename from scripts/docker/shutdown.sh rename to scripts/git/configure_isort_paths.sh index 81f32c69..318fae8a 100755 --- a/scripts/docker/shutdown.sh +++ b/scripts/git/configure_isort_paths.sh @@ -18,13 +18,16 @@ # License for the specific language governing permissions and limitations # under the License. -thisdir=$(dirname "$(readlink -f "$0")") +# Updates the src_paths setting in the .isort.cfg file at the top +# level. See that file for more details. -# stop isaac container if it exists -docker stop isaac 2>/dev/null -docker rm isaac 2>/dev/null +thisdir=$(dirname "$0") +srcdir=$(cd $thisdir/../.. && pwd) -# stop all docker-compose combinations -docker-compose -f ${thisdir}/docker_compose/ros.docker-compose.yml -f ${thisdir}/docker_compose/idi.docker-compose.yml -f ${thisdir}/docker_compose/mast.docker_compose.yml -f ${thisdir}/docker_compose/analyst.docker-compose.yml down \ -||docker-compose -f ${thisdir}/docker_compose/ros.docker-compose.yml -f ${thisdir}/docker_compose/idi.docker-compose.yml -f ${thisdir}/docker_compose/mast.docker_compose.yml down \ -|| docker-compose -f ${thisdir}/docker_compose/ros.docker-compose.yml -f ${thisdir}/docker_compose/idi.docker-compose.yml down +cd $srcdir + +# Generate a comma-separated list of folders containing *.py files +pydirs=$(find . -name "*.py" -print0 | xargs -0 dirname | cut -c3- | sort | uniq | paste -sd "," -) + +# Overwrite the src_paths line in the config file to use the list +perl -i -ple "if (/^src_paths = /) { \$_ = 'src_paths = $pydirs'; }" .isort.cfg diff --git a/scripts/git/pre-commit.linter_python b/scripts/git/pre-commit.linter_python index 9fbb6774..89574084 100755 --- a/scripts/git/pre-commit.linter_python +++ b/scripts/git/pre-commit.linter_python @@ -18,10 +18,10 @@ # License for the specific language governing permissions and limitations # under the License. -files=$(git diff --cached --name-only | grep '\.py$') +py_changed=$(git diff --diff-filter=d --cached --name-only | grep '\.py$') -# If files is empty exit success -if [ -z "${files}" ]; then +# If py_changed is empty exit success +if [ -z "${py_changed}" ]; then echo "==================================================" echo " No Python files changed, no checks needed." exit 0 @@ -47,21 +47,36 @@ fi echo "==================================================" echo " Analysing python code style with 'black'." # This check the files but they will not be commited -if `black . --include ${files} --check --quiet`; then +if `black . --include ${py_changed} --check --quiet`; then echo "Linter checks using 'black' passed." else echo "Errors detected with 'black'. Fixing them. Try to add and commit your files again." - black . --include ${files} + black . --include ${py_changed} failed_lint=true fi echo "==================================================" echo " Analysing python code style with 'isort'." -if $(isort ${files} --extend-skip cmake --profile black --diff --check-only --quiet >/dev/null); then + +# We're running isort recursively on the top-level folder (not +# limiting it to the changed files) in order to match how it is +# invoked in the CI workflow. This is because isort determines what +# imports are treated as first party, and therefore should be grouped +# separately, in part based on what files it is asked to process. We +# definitely don't want to have any disagreement where this pre-commit +# hook wants things grouped one way but the CI workflow says that's +# wrong. Note that our intention is for isort to treat all imports +# found in the astrobee repo as first party, and we specify that using +# the src_paths setting in the .isort.cfg file. But we should still +# run isort consistently in both places to avoid disagreement, which +# could happen for example if the .isort.cfg src_paths list gets out +# of date. + +if $(isort . --extend-skip cmake --profile black --diff --check-only --quiet >/dev/null); then echo "Linter checks using 'isort' passed." else echo "Errors detected with 'isort'. Fixing them. Try to add and commit your files again." - isort ${files} --extend-skip cmake --profile black >/dev/null + isort . --extend-skip cmake --profile black >/dev/null failed_lint=true fi diff --git a/scripts/install_to_astrobee.sh b/scripts/install_to_astrobee.sh index b7f596db..ee3c2490 100755 --- a/scripts/install_to_astrobee.sh +++ b/scripts/install_to_astrobee.sh @@ -79,17 +79,6 @@ if [[ ${FREEFLYER_TARGETS,,} =~ 'mlp' ]]; then fi fi -if [[ ${FREEFLYER_TARGETS,,} =~ 'llp' ]]; then - # Install to LLP - if [[ "${llp_ips[${robot_index}]}" != "0.0.0.0" ]]; then # for dock, skip this step - echo "Copying files to LLP..." - if ! rsync -azh --delete --info=progress2 $target/ astrobee@llp_iss_${config_ver}:${FREEFLYER_INSTALL_DIR} - then - exit 1 - fi - fi -fi - if [ -n "$ip_addr" ]; then echo '*** NOTE ***' echo 'Be sure to export the following before running anything from this machine: ' diff --git a/scripts/setup/build_install_dependencies.sh b/scripts/setup/build_install_dependencies.sh index 22a40ff5..06717a3d 100755 --- a/scripts/setup/build_install_dependencies.sh +++ b/scripts/setup/build_install_dependencies.sh @@ -23,14 +23,18 @@ DEP_LOC=$(dirname "$(readlink -f "$0")")/dependencies -sudo apt-get install -y libgtest-dev -cd /usr/src/gtest -sudo cmake CMakeLists.txt -sudo make -sudo cp *.a /usr/lib +sudo apt-get install -y unzip libgtest-dev + +# Comes pre-built in Ubuntu 20.04 +if [ "$(lsb_release -sr)" = "18.04" ]; then + cd /usr/src/gtest + sudo cmake CMakeLists.txt + sudo make + sudo cp *.a /usr/lib +fi cd ${DEP_LOC} ./build_install_gp.sh || exit 1 cd ${DEP_LOC} -./build_install_torch.sh || exit 1 \ No newline at end of file +./build_install_torch.sh || exit 1 diff --git a/scripts/setup/dependencies/build_install_torch.sh b/scripts/setup/dependencies/build_install_torch.sh index 1df93a07..b9d4c77d 100755 --- a/scripts/setup/dependencies/build_install_torch.sh +++ b/scripts/setup/dependencies/build_install_torch.sh @@ -23,8 +23,18 @@ PACKAGE_NAME=libtorch if [ -d $PACKAGE_NAME ]; then rm -rf $PACKAGE_NAME fi -wget https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-1.5.0%2Bcpu.zip -sudo unzip libtorch-cxx11-abi-shared-with-deps-1.5.0+cpu.zip -d /usr/include -echo 'export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/usr/include/libtorch/share/cmake/Torch' >> ~/.bashrc -export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/usr/include/libtorch/share/cmake/Torch +wget --quiet https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-1.5.0%2Bcpu.zip +sudo unzip -q libtorch-cxx11-abi-shared-with-deps-1.5.0+cpu.zip -d /usr/include +## Torch CMAKE_PREFIX_PATH +# Astrobee always augments the CMAKE_PREFIX_PATH in .bashrc before testing +# if zsh is in use. We'll just add a conditional extension to every shell +# rc file we find (currently only looking at .bashrc and .zshrc) +cmake_isaac_torch_path=/usr/include/libtorch/share/cmake/Torch +for shell_cfg in "~/.bashrc" "~/.zshrc"; do + if [[ -f ${shell_cfg} ]] && [ $(grep -cF ${cmake_isaac_torch_path} ${shell_cfg}) -eq 0 ]; then + echo -e '\n## ISAAC Dependency - Torch CMAKE Path\n' >> ${shell_cfg} + echo 'if [[ ":$CMAKE_PREFIX_PATH:" != *":'${cmake_isaac_torch_path}':"* ]]; then CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH:+"$CMAKE_PREFIX_PATH:"}'${cmake_isaac_torch_path}'"; fi' >> ${shell_cfg} + fi +done +echo "Torch added to CMAKE_PREFIX_PATH in shell config file, source ~/.$(basename ${SHELL})rc before building" diff --git a/scripts/setup/install_desktop_packages.sh b/scripts/setup/install_desktop_packages.sh index b79cd066..21cef37a 100755 --- a/scripts/setup/install_desktop_packages.sh +++ b/scripts/setup/install_desktop_packages.sh @@ -52,12 +52,12 @@ then if [ "${NO_TUNNEL}" -eq 1 ]; then echo "Getting the custom Debian without tunnel" - sudo /bin/bash -c "echo \"deb [arch=amd64] http://astrobee.ndc.nasa.gov/software ${DIST} main\" > $arssrc" || exit 1 - sudo /bin/bash -c "echo \"deb-src http://astrobee.ndc.nasa.gov/software ${DIST} main\" >> $arssrc" || exit 1 + sudo /bin/bash -c "echo \"deb [arch=amd64] http://astrobee.ndc.nasa.gov/software_new ${DIST} main\" > $arssrc" || exit 1 + sudo /bin/bash -c "echo \"deb-src http://astrobee.ndc.nasa.gov/software_new ${DIST} main\" >> $arssrc" || exit 1 else echo "Tunnelling to get the custom Debian" - sudo /bin/bash -c "echo \"deb [arch=amd64] http://127.0.0.1:8765/${DIST} ${DIST} main\" > $arssrc" || exit 1 - sudo /bin/bash -c "echo \"deb-src http://127.0.0.1:8765/software xenial main\" >> $arssrc" || exit 1 + sudo /bin/bash -c "echo \"deb [arch=amd64] http://127.0.0.1:8765/software_new ${DIST} main\" > $arssrc" || exit 1 + sudo /bin/bash -c "echo \"deb-src http://127.0.0.1:8765/software_new ${DIST} main\" >> $arssrc" || exit 1 ssh -N -L 8765:astrobee.ndc.nasa.gov:80 ${username}m.ndc.nasa.gov & fi diff --git a/shared/isaac_util/include/isaac_util/isaac_names.h b/shared/isaac_util/include/isaac_util/isaac_names.h index 377f1c66..0b9dc502 100644 --- a/shared/isaac_util/include/isaac_util/isaac_names.h +++ b/shared/isaac_util/include/isaac_util/isaac_names.h @@ -38,6 +38,7 @@ #define TOPIC_BEHAVIORS_INSPECTION_STATE "beh/inspection/state" #define SERVICE_BEHAVIORS_INSPECTION_SET_STATE "beh/inspection/set_state" #define ACTION_BEHAVIORS_INSPECTION "beh/inspection" +#define TOPIC_BEHAVIORS_INSPECTION_MARKERS "beh/inspection/markers" #define NODE_CARGO "cargo" #define TOPIC_BEHAVIORS_CARGO_STATE "beh/cargo/state"