From de7a887e5187376bd87d5d1a8aafedb7a2a833d4 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 25 Jul 2022 18:41:57 +0900 Subject: [PATCH 01/77] [jsk_unitree_startup/cross] Add python3-venv for catkin virtualenv --- jsk_unitree_robot/cross/docker/deb-packages.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/jsk_unitree_robot/cross/docker/deb-packages.txt b/jsk_unitree_robot/cross/docker/deb-packages.txt index 84684bfd7c..f4b65059c9 100644 --- a/jsk_unitree_robot/cross/docker/deb-packages.txt +++ b/jsk_unitree_robot/cross/docker/deb-packages.txt @@ -1125,6 +1125,7 @@ python3-setuptools python3-simplejson python3-six python3-urllib3 +python3-venv python3-webencodings python3-wheel python3-xdg From f9d4baccc8d87973a118e2a3497225e644b89898 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 11:41:25 +0900 Subject: [PATCH 02/77] [jsk_unitree_startup/cross] Use sitecustomize.py instead of setting PYTHONPATH --- jsk_unitree_robot/cross/build_ros1.sh | 2 ++ jsk_unitree_robot/cross/build_user.sh | 4 ++- .../ros1_dependencies_setup.bash | 6 +++-- .../cross/startup_scripts/sitecustomize.py | 26 +++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 jsk_unitree_robot/cross/startup_scripts/sitecustomize.py diff --git a/jsk_unitree_robot/cross/build_ros1.sh b/jsk_unitree_robot/cross/build_ros1.sh index 05712d2b1d..008db39f2a 100755 --- a/jsk_unitree_robot/cross/build_ros1.sh +++ b/jsk_unitree_robot/cross/build_ros1.sh @@ -50,6 +50,7 @@ docker run -it --rm \ -e INSTALL_ROOT=${INSTALL_ROOT} \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies:ro \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies_setup.bash:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies_setup.bash:ro \ + -v ${PWD}/startup_scripts/sitecustomize.py:/usr/lib/python2.7/sitecustomize.py:ro \ -v ${HOST_INSTALL_ROOT}/ros1_inst:/opt/jsk/${INSTALL_ROOT}/ros1_inst:rw \ -v ${PWD}/${SOURCE_ROOT}:/home/user/${SOURCE_ROOT}:rw \ ros1-unitree:${TARGET_MACHINE} \ @@ -69,3 +70,4 @@ docker run -it --rm \ " 2>&1 | tee ${TARGET_MACHINE}_build_ros1.log cp ${PWD}/startup_scripts/system_setup.bash ${HOST_INSTALL_ROOT}/ +cp ${PWD}/startup_scripts/sitecustomize.py ${HOST_INSTALL_ROOT}/ diff --git a/jsk_unitree_robot/cross/build_user.sh b/jsk_unitree_robot/cross/build_user.sh index f18831c411..d0855e30a1 100755 --- a/jsk_unitree_robot/cross/build_user.sh +++ b/jsk_unitree_robot/cross/build_user.sh @@ -50,6 +50,8 @@ docker run -it --rm \ -v ${HOST_INSTALL_ROOT}/ros1_inst:/opt/jsk/${INSTALL_ROOT}/ros1_inst:ro \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies_setup.bash:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies_setup.bash:ro \ -v ${HOST_INSTALL_ROOT}/system_setup.bash:/opt/jsk/${INSTALL_ROOT}/system_setup.bash:ro \ + -v ${HOST_INSTALL_ROOT}/sitecustomize.py:/usr/lib/python2.7/sitecustomize.py:ro \ + -v ${HOST_INSTALL_ROOT}/sitecustomize.py:/usr/lib/python3.6/sitecustomize.py:ro \ -v ${PWD}/${SOURCE_ROOT}:/opt/jsk/User:rw \ -v ${PWD}/rosinstall_generator_unreleased.py:/home/user/rosinstall_generator_unreleased.py:ro \ ros1-unitree:${TARGET_MACHINE} \ @@ -59,7 +61,7 @@ docker run -it --rm \ set -xeuf -o pipefail && \ cd /opt/jsk/User && \ [ ${UPDATE_SOURCE_ROOT} -eq 0 ] || ROS_PACKAGE_PATH=src:\${ROS_PACKAGE_PATH} /home/user/rosinstall_generator_unreleased.py jsk_${TARGET_ROBOT}_startup ${TARGET_ROBOT}eus --rosdistro melodic --exclude RPP --exclude mongodb_store | tee user.repos && \ - [ ${UPDATE_SOURCE_ROOT} -eq 0 -o -z \"\$(cat user.repos)\" ] || PYTHONPATH= vcs import src < user.repos && \ + [ ${UPDATE_SOURCE_ROOT} -eq 0 -o -z \"\$(cat user.repos)\" ] || vcs import src < user.repos && \ catkin build jsk_${TARGET_ROBOT}_startup ${TARGET_ROBOT}eus -s -vi \ --cmake-args -DCATKIN_ENABLE_TESTING=FALSE \ " 2>&1 | tee ${TARGET_MACHINE}_build_user.log diff --git a/jsk_unitree_robot/cross/startup_scripts/ros1_dependencies_setup.bash b/jsk_unitree_robot/cross/startup_scripts/ros1_dependencies_setup.bash index bb83142ace..2ab07f4d83 100755 --- a/jsk_unitree_robot/cross/startup_scripts/ros1_dependencies_setup.bash +++ b/jsk_unitree_robot/cross/startup_scripts/ros1_dependencies_setup.bash @@ -5,14 +5,16 @@ export CMAKE_PREFIX_PATH="/opt/jsk/System/ros1_dependencies:${CMAKE_PREFIX_PATH}" export PKG_CONFIG_PATH="/opt/jsk/System/ros1_dependencies/lib/pkgconfig:${PKG_CONFIG_PATH}" export LD_LIBRARY_PATH="/opt/jsk/System/ros1_dependencies/lib:${LD_LIBRARY_PATH}" -export PYTHONPATH="/opt/jsk/System/ros1_dependencies/lib/python2.7/site-packages:${PYTHONPATH}" +# Python's sys.path is automatically set in /usr/lib/python2.7/sitecustomize.py +# export PYTHONPATH="/opt/jsk/System/ros1_dependencies/lib/python2.7/site-packages:${PYTHONPATH}" # GI : for gir1.2-gstreamer-1.0, which is installed by ros1_dependencies_build_scripts/0006-gstreamer export GI_TYPELIB_PATH="/opt/jsk/System/ros1_dependencies/lib/girepository-1.0" # Python export LD_LIBRARY_PATH="/opt/jsk/System/Python/lib:${LD_LIBRARY_PATH}" -export PYTHONPATH="/opt/jsk/System/Python/lib/python2.7/site-packages:${PYTHONPATH}" +# Python's sys.path is automatically set in /usr/lib/python2.7/sitecustomize.py +# export PYTHONPATH="/opt/jsk/System/Python/lib/python2.7/site-packages:${PYTHONPATH}" export PATH="/opt/jsk/System/Python/bin:${PATH}" diff --git a/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py b/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py new file mode 100644 index 0000000000..f37cd9bfe9 --- /dev/null +++ b/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py @@ -0,0 +1,26 @@ +# install the apport exception handler if available +import site +import sys +import os.path as osp + +site.PREFIXES += [ + '/opt/jsk/System/Python/', + '/opt/jsk/System/ros1_dependencies/', + '/opt/jsk/System/ros1_inst/', +] + +paths = [] +for path in site.getsitepackages(): + site_package = osp.join(osp.dirname(path), 'site-packages') + if site_package != path and osp.exists(site_package): + paths.append(site_package) + else: + paths.append(path) + +sys.path.extend(paths) +try: + import apport_python_hook +except ImportError: + pass +else: + apport_python_hook.install() From 554a87bb09f05793adc3a0d29fc82159d55cc908 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 16:05:12 +0900 Subject: [PATCH 03/77] [jsk_unitree_startup/cross] Recursively append python site/dist-package paths --- .../cross/startup_scripts/sitecustomize.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py b/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py index f37cd9bfe9..6e0abdf861 100644 --- a/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py +++ b/jsk_unitree_robot/cross/startup_scripts/sitecustomize.py @@ -1,6 +1,7 @@ # install the apport exception handler if available import site import sys +import os import os.path as osp site.PREFIXES += [ @@ -9,13 +10,25 @@ '/opt/jsk/System/ros1_inst/', ] + +def recursively_listup_paths(path): + paths = [] + for sub_path in os.listdir(path): + sub_full_path = osp.join(path, sub_path) + if osp.isdir(sub_full_path) and (sub_path.endswith('.egg-info') or sub_path.endswith('.egg')): + paths.append(sub_full_path) + return paths + + paths = [] for path in site.getsitepackages(): + if osp.exists(path): + paths.append(path) + paths.extend(recursively_listup_paths(path)) site_package = osp.join(osp.dirname(path), 'site-packages') if site_package != path and osp.exists(site_package): paths.append(site_package) - else: - paths.append(path) + paths.extend(recursively_listup_paths(site_package)) sys.path.extend(paths) try: From f52c4de1573329e1ce40c532732c9cf0a522068b Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 16:06:55 +0900 Subject: [PATCH 04/77] [jsk_unitree_startup/cross] Add google_chat_ros --- jsk_unitree_robot/cross/repos/unitree.repos | 8 ++++++++ jsk_unitree_robot/jsk_unitree_startup/package.xml | 1 + 2 files changed, 9 insertions(+) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index 8e8e056cc1..d6a9239589 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -21,3 +21,11 @@ repositories: type: git url: https://github.com/Affonso-Gui/app_manager version: enable-parallel-run-apps + google_chat_ros: + type: git + url: https://github.com/mqcmd196/jsk_3rdparty + version: google_chat_ros + catkin_virtualenv: + type: git + url: https://github.com/locusrobotics/catkin_virtualenv + version: 0.8.0 diff --git a/jsk_unitree_robot/jsk_unitree_startup/package.xml b/jsk_unitree_robot/jsk_unitree_startup/package.xml index 9723686b9a..59e3a350d7 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/package.xml +++ b/jsk_unitree_robot/jsk_unitree_startup/package.xml @@ -16,6 +16,7 @@ rosserial_python teleop_twist_joy respeaker_ros + google_chat_ros diagnostic_aggregator From dd38754b9cc4383edd00e13558399d2076b17ce4 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 17:21:28 +0900 Subject: [PATCH 05/77] [jsk_unitree_startup/cross] Use bug-fix version of catkin-virtualenv --- jsk_unitree_robot/cross/repos/unitree.repos | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index d6a9239589..8fa7e17eb0 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -27,5 +27,5 @@ repositories: version: google_chat_ros catkin_virtualenv: type: git - url: https://github.com/locusrobotics/catkin_virtualenv - version: 0.8.0 + url: https://github.com/iory/catkin_virtualenv + version: avoid-catkin_run_tests_target From c0b2cc82ef3fd1a211f2387593f8ac865a156466 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 17:22:06 +0900 Subject: [PATCH 06/77] [jsk_unitree_startup/cross] Install python3 dependencies for catkin_virtualenv --- jsk_unitree_robot/cross/build_ros1_dependencies.sh | 5 ++++- jsk_unitree_robot/cross/repos/go1_requirements_python3.txt | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 jsk_unitree_robot/cross/repos/go1_requirements_python3.txt diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index 56b8f6a418..fd718e4b8e 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -24,6 +24,7 @@ cp repos/ros1_dependencies.repos ${SOURCE_ROOT}/ mkdir -p ${HOST_INSTALL_ROOT}/Python cp repos/go1_requirements.txt ${SOURCE_ROOT}/go1_requirements.txt +cp repos/go1_requirements_python3.txt ${SOURCE_ROOT}/go1_requirements_python3.txt docker run -it --rm \ -u $(id -u $USER) \ @@ -40,8 +41,10 @@ docker run -it --rm \ for script_file in \$(ls /home/user/ros1_dependencies_build_scripts/|sort); do /home/user/ros1_dependencies_build_scripts/\$script_file || exit 1; done && \ + pip3 install -U --user pip && \ pip install -U --user pip && \ export PYTHONPATH=\"/opt/jsk/System/ros1_dependencies/lib/python2.7/site-packages\" && \ export PKG_CONFIG_PATH=\"/opt/jsk/${INSTALL_ROOT}/ros1_dependencies/lib/pkgconfig\" && \ - ~/.local/bin/pip install --prefix=/opt/jsk/${INSTALL_ROOT}/Python -r /home/user/ros1_dependencies_sources/go1_requirements.txt \ + ~/.local/bin/pip install --prefix=/opt/jsk/${INSTALL_ROOT}/Python -r /home/user/ros1_dependencies_sources/go1_requirements.txt && \ + PYTHONPATH= ~/.local/bin/pip3 install --prefix=/opt/jsk/${INSTALL_ROOT}/Python -r /home/user/ros1_dependencies_sources/go1_requirements_python3.txt \ " 2>&1 | tee ${TARGET_MACHINE}_build_ros1_dependencies.log diff --git a/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt b/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt new file mode 100644 index 0000000000..3f9f5d0b12 --- /dev/null +++ b/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt @@ -0,0 +1,2 @@ +# needeed by catkin_virtualenv +catkin-pkg From 533783844bf0e0f97328a20920123e583e447abf Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 17:55:06 +0900 Subject: [PATCH 07/77] [jsk_unitree_startup/cross] Add JSK_UNITREE_COMPILE_PARALLEL option to change number of parallel jobs --- jsk_unitree_robot/cross/build_ros1_dependencies.sh | 1 + .../cross/ros1_dependencies_build_scripts/1001-urdfdom-headers | 2 +- .../cross/ros1_dependencies_build_scripts/1002-urdfdom | 2 +- .../cross/ros1_dependencies_build_scripts/1003-assimp | 2 +- .../cross/ros1_dependencies_build_scripts/1004-colladadom | 2 +- .../cross/ros1_dependencies_build_scripts/1005-yaml-cpp | 2 +- .../cross/ros1_dependencies_build_scripts/1007-snappy | 2 +- .../cross/ros1_dependencies_build_scripts/1008-opencv | 2 +- .../cross/ros1_dependencies_build_scripts/1009-python-sip | 2 +- .../cross/ros1_dependencies_build_scripts/1024-liblcm | 2 +- .../cross/ros1_dependencies_build_scripts/1025-alsa-lib | 2 +- .../cross/ros1_dependencies_build_scripts/1026-db5 | 1 + .../cross/ros1_dependencies_build_scripts/1028-libportaudio2 | 2 +- .../cross/ros1_dependencies_build_scripts/1030-flac | 2 +- .../cross/ros1_dependencies_build_scripts/1031-libncurses | 2 +- .../cross/ros1_dependencies_build_scripts/1032-speech-tools | 1 + .../cross/ros1_dependencies_build_scripts/1033-festival | 1 + 17 files changed, 17 insertions(+), 13 deletions(-) diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index fd718e4b8e..3fb19f5e01 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -29,6 +29,7 @@ cp repos/go1_requirements_python3.txt ${SOURCE_ROOT}/go1_requirements_python3.tx docker run -it --rm \ -u $(id -u $USER) \ -e INSTALL_ROOT=${INSTALL_ROOT} \ + -e JSK_UNITREE_COMPILE_PARALLEL=${JSK_UNITREE_COMPILE_PARALLEL} \ -v ${PWD}/ros1_dependencies_build_scripts:/home/user/ros1_dependencies_build_scripts:ro \ -v ${PWD}/${SOURCE_ROOT}:/home/user/ros1_dependencies_sources:rw \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk//${INSTALL_ROOT}/ros1_dependencies:rw \ diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers index f9e2b9e3e0..21a2f3f06b 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers @@ -10,4 +10,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ../../src/urdfdom-headers/urdfdom_headers-1.0.0 -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom index e4b8ea0d15..3357529dec 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom @@ -10,4 +10,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ../../src/urdfdom/urdfdom-1.0.0 -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp index 3dc23b4619..a33874a214 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp @@ -23,4 +23,4 @@ cmake \ -DASSIMP_ENABLE_BOOST_WORKAROUND=OFF \ ${SOURCE_DIR} -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom index a74c303a62..d00d1d4feb 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom @@ -27,4 +27,4 @@ cmake \ -DOPT_DOUBLE_PRECISION=ON \ ${SOURCE_DIR} -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp index 0f1bcd80a0..509ed0bec3 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp @@ -19,4 +19,4 @@ cmake \ -DYAML_CPP_BUILD_TOOLS=ON \ ${SOURCE_DIR} -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy index a2a46b22b8..9644678b08 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy @@ -18,4 +18,4 @@ cmake \ -DBUILD_SHARED_LIBS=on \ ${SOURCE_DIR} -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv index 4f52260bf5..7bd2ce8cab 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv @@ -50,4 +50,4 @@ cmake \ -DOPENCV_CONFIG_INSTALL_PATH=lib/cmake/OpenCV \ ${SOURCE_DIR} -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip index 5920f9e9e1..7b8d2e6dfd 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip @@ -18,4 +18,4 @@ python \ -e /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/include \ -b /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/bin \ -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm index 54bb3e40bc..ae2ef2f7d6 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm @@ -20,4 +20,4 @@ automake --add-missing autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib index 0b548baf83..4e9fbe46b5 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib @@ -16,4 +16,4 @@ cd ${SOURCE_DIR} ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 index f82fb4fa88..eb39e405da 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 @@ -16,4 +16,5 @@ cd ${SOURCE_DIR}/build_unix ../dist/configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies --build $(uname -m)-unknown-linux-gnu --enable-cxx --enable-compat185 +# Can be compiled safely with j1. make -j1 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 index d13d19e6cd..4b6db9066c 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 @@ -20,4 +20,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ${SOURCE_DIR} -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac index 0455aab6ba..186f2c2193 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac @@ -18,4 +18,4 @@ automake --add-missing autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses index 7638ed7532..08242b8135 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses @@ -14,4 +14,4 @@ cd ${SOURCE_DIR} ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -with-shared -make -j4 install +make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools index c2447e8fd6..62a5b3a116 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools @@ -30,6 +30,7 @@ export PROJECT_MAJOR_VERSION=2 ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies +# Can be compiled safely with j1. make -j1 PULSEAUDIO=none cp lib/*.a /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/lib diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival index 8060ba8551..4873594a46 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival @@ -28,6 +28,7 @@ libtoolize autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies +# Can be compiled safely with j1. EST="../speech_tools" make -j1 all cp src/main/festival src/main/festival_client bin/text2wave /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/bin/ From 785af9ec1688e502b442af478565d22d2b65646f Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 21:58:06 +0900 Subject: [PATCH 08/77] [jsk_unitree_startup/cross] Install sitecustomize.py for real unitree robot --- jsk_unitree_robot/cross/install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index e05fa2f36f..e7d4a60fb4 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -64,6 +64,12 @@ function copy_data () { ssh-keygen -f "${HOME}/.ssh/known_hosts" -R "${hostname}" || echo "OK" sshpass -p $PASS ssh -o StrictHostKeyChecking=no ${user}@${hostname} exit + if [[ "${TARGET_DIRECTORY}" == "System" ]]; then + sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/sitecustomize.py ${user}@${hostname}:/tmp/sitecustomize.py + sshpass -p $PASS ssh -t ${user}@${hostname} "sudo cp -f /tmp/sitecustomize.py /usr/lib/python2.7/sitecustomize.py" + sshpass -p $PASS ssh -t ${user}@${hostname} "sudo cp -f /tmp/sitecustomize.py /usr/lib/python3/sitecustomize.py" + fi + # cehck disk space echo "Copy ${TARGET_MACHINE}_${TARGET_DIRECTORY} ...." echo "===" From 1e045b1082fa203156c71d594135b72c51882b4f Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 23:40:04 +0900 Subject: [PATCH 09/77] [jsk_unitree_startup] Fixed install.sh command description. -d is correct instead of -D --- jsk_unitree_robot/cross/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/README.md b/jsk_unitree_robot/cross/README.md index 4c38e5c985..451e5766ff 100644 --- a/jsk_unitree_robot/cross/README.md +++ b/jsk_unitree_robot/cross/README.md @@ -29,7 +29,7 @@ Caution!!! It will take more than a few hours !! So for JSK users, download the Run following command to copy ROS1 base sytem to Go1 onboard computer. This should be done only in the first time. So normally user do not have to run this command ``` -./install.sh -p 123 -D System +./install.sh -p 123 -d System ``` ### Build `jsk_unitree_robot` software on Docker From bba78e2cae3a3b72dc8ed9442093c4dab6c47c52 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 23:40:53 +0900 Subject: [PATCH 10/77] [jsk_unitree_startup] Use perl instead of 'grep -Po' --- jsk_unitree_robot/cross/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index e7d4a60fb4..d5db4cd71c 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -54,7 +54,7 @@ function copy_data () { fi # Check if robot is reachable - reachability=$(ping -c4 ${hostname} 2>/dev/null | awk '/---/,0' | grep -Po '[0-9]{1,3}(?=% packet loss)') + reachability=$(ping -c4 ${hostname} 2>/dev/null | awk '/---/,0' | perl -nle'print $& while m{[0-9]{1,3}(?=% packet loss)}g') if [ -z "$reachability" ] || [ "$reachability" == 100 ]; then echo "ERROR: ${hostname} unreachable" 1>&2 exit 2 From b1c13cd31488dac88cc544742dd1350e2f76b115 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 23:43:45 +0900 Subject: [PATCH 11/77] [jsk_unitree_startup/cross] Install LTE settings only TARGET_DIRECTORY is User case --- jsk_unitree_robot/cross/install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index d5db4cd71c..c06fba1a87 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -113,12 +113,12 @@ function copy_data () { sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find respeaker_ros)/config/60-respeaker.rules /etc/udev/rules.d/60-respeaker.rules" # sshpass -p $PASS ssh -t ${user}@${hostname} "ls -al /etc/udev/rules.d/; sudo systemctl restart udev" - fi - # enable Internet with USB LTE module - if [[ "${hostname}" == "192.168.123.161" ]]; then - sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" - sshpass -p $PASS ssh -t ${user}@${hostname} "sudo systemctl restart dhcpcd" + # enable Internet with USB LTE module + if [[ "${hostname}" == "192.168.123.161" ]]; then + sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" + sshpass -p $PASS ssh -t ${user}@${hostname} "sudo systemctl restart dhcpcd" + fi fi set +x } From f55266b60ca34e17a8764a5cb0a6a2dd9ebe0293 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 26 Jul 2022 23:44:29 +0900 Subject: [PATCH 12/77] [jsk_unitree_startup/cross] Use sshpass instead of /usr/bin/sshpass --- jsk_unitree_robot/cross/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index c06fba1a87..6b8d5df4bd 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -98,9 +98,9 @@ function copy_data () { sshpass -p $PASS ssh -t ${user}@${hostname} "test -e /opt/jsk" || \ sshpass -p $PASS ssh -t ${user}@${hostname} "sudo mkdir -p /opt/jsk && sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /opt/jsk && ls -al /opt/jsk" - rsync --rsh="/usr/bin/sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded --exclude "*.pyc" --exclude "^logs/" ${TARGET_MACHINE}_${TARGET_DIRECTORY}/ ${hostname}:/opt/jsk/${TARGET_DIRECTORY} + rsync --rsh="sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded --exclude "*.pyc" --exclude "^logs/" ${TARGET_MACHINE}_${TARGET_DIRECTORY}/ ${hostname}:/opt/jsk/${TARGET_DIRECTORY} if [[ "${TARGET_DIRECTORY}" == "User" ]]; then - rsync --rsh="/usr/bin/sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded ../jsk_unitree_startup/autostart/ ${hostname}:Unitree/autostart/jsk_startup + rsync --rsh="sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded ../jsk_unitree_startup/autostart/ ${hostname}:Unitree/autostart/jsk_startup # https://stackoverflow.com/questions/23395363/make-patch-return-0-when-skipping-an-already-applied-patch # On Air, we need to start unitree_bringup at 129.168.123.13 From 1c21dd2f0da85e39e2c6096321c1b70be081614a Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 27 Jul 2022 00:51:57 +0900 Subject: [PATCH 13/77] [jsk_unitree_startup/cross] Use bug-fix branch for enabling aarch64 (diaglogflow) --- jsk_unitree_robot/cross/repos/unitree.repos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index 8fa7e17eb0..b5c40d7b01 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -23,7 +23,7 @@ repositories: version: enable-parallel-run-apps google_chat_ros: type: git - url: https://github.com/mqcmd196/jsk_3rdparty + url: https://github.com/iory/jsk_3rdparty version: google_chat_ros catkin_virtualenv: type: git From c60b7d97a9077498bf54068c99e01ac6b1779531 Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 27 Jul 2022 02:05:37 +0900 Subject: [PATCH 14/77] [jsk_unitree_startup/cross] Set default value 4 to JSK_UNITREE_COMPILE_PARALLEL --- jsk_unitree_robot/cross/build_ros1_dependencies.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index 3fb19f5e01..e36626291c 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -4,6 +4,7 @@ TARGET_MACHINE="${TARGET_MACHINE:-arm64v8}" HOST_INSTALL_ROOT="${BASE_ROOT:-${PWD}}/"${TARGET_MACHINE}_System INSTALL_ROOT=System SOURCE_ROOT=${TARGET_MACHINE}_ws_ros1_dependencies_sources +JSK_UNITREE_COMPILE_PARALLEL="${JSK_UNITREE_COMPILE_PARALLEL:-4}" if [ -e "${SOURCE_ROOT}" ]; then echo "WARNING: Source directory is found ${SOURCE_ROOT}" 1>&2 From ce8e63a1ea34f9c214833470d794723ed9792d78 Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 27 Jul 2022 14:48:53 +0900 Subject: [PATCH 15/77] Revert "[jsk_unitree_startup/cross] Add JSK_UNITREE_COMPILE_PARALLEL option to change number of parallel jobs" This reverts commit 7f9dd4a2a7a8eab21791dd8f1a7e662a088a515d. --- jsk_unitree_robot/cross/build_ros1_dependencies.sh | 1 - .../cross/ros1_dependencies_build_scripts/1001-urdfdom-headers | 2 +- .../cross/ros1_dependencies_build_scripts/1002-urdfdom | 2 +- .../cross/ros1_dependencies_build_scripts/1003-assimp | 2 +- .../cross/ros1_dependencies_build_scripts/1004-colladadom | 2 +- .../cross/ros1_dependencies_build_scripts/1005-yaml-cpp | 2 +- .../cross/ros1_dependencies_build_scripts/1007-snappy | 2 +- .../cross/ros1_dependencies_build_scripts/1008-opencv | 2 +- .../cross/ros1_dependencies_build_scripts/1009-python-sip | 2 +- .../cross/ros1_dependencies_build_scripts/1024-liblcm | 2 +- .../cross/ros1_dependencies_build_scripts/1025-alsa-lib | 2 +- .../cross/ros1_dependencies_build_scripts/1026-db5 | 1 - .../cross/ros1_dependencies_build_scripts/1028-libportaudio2 | 2 +- .../cross/ros1_dependencies_build_scripts/1030-flac | 2 +- .../cross/ros1_dependencies_build_scripts/1031-libncurses | 2 +- .../cross/ros1_dependencies_build_scripts/1032-speech-tools | 1 - .../cross/ros1_dependencies_build_scripts/1033-festival | 1 - 17 files changed, 13 insertions(+), 17 deletions(-) diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index e36626291c..e1fcc8b020 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -30,7 +30,6 @@ cp repos/go1_requirements_python3.txt ${SOURCE_ROOT}/go1_requirements_python3.tx docker run -it --rm \ -u $(id -u $USER) \ -e INSTALL_ROOT=${INSTALL_ROOT} \ - -e JSK_UNITREE_COMPILE_PARALLEL=${JSK_UNITREE_COMPILE_PARALLEL} \ -v ${PWD}/ros1_dependencies_build_scripts:/home/user/ros1_dependencies_build_scripts:ro \ -v ${PWD}/${SOURCE_ROOT}:/home/user/ros1_dependencies_sources:rw \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk//${INSTALL_ROOT}/ros1_dependencies:rw \ diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers index 21a2f3f06b..f9e2b9e3e0 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers @@ -10,4 +10,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ../../src/urdfdom-headers/urdfdom_headers-1.0.0 -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom index 3357529dec..e4b8ea0d15 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom @@ -10,4 +10,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ../../src/urdfdom/urdfdom-1.0.0 -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp index a33874a214..3dc23b4619 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp @@ -23,4 +23,4 @@ cmake \ -DASSIMP_ENABLE_BOOST_WORKAROUND=OFF \ ${SOURCE_DIR} -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom index d00d1d4feb..a74c303a62 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom @@ -27,4 +27,4 @@ cmake \ -DOPT_DOUBLE_PRECISION=ON \ ${SOURCE_DIR} -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp index 509ed0bec3..0f1bcd80a0 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp @@ -19,4 +19,4 @@ cmake \ -DYAML_CPP_BUILD_TOOLS=ON \ ${SOURCE_DIR} -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy index 9644678b08..a2a46b22b8 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy @@ -18,4 +18,4 @@ cmake \ -DBUILD_SHARED_LIBS=on \ ${SOURCE_DIR} -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv index 7bd2ce8cab..4f52260bf5 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv @@ -50,4 +50,4 @@ cmake \ -DOPENCV_CONFIG_INSTALL_PATH=lib/cmake/OpenCV \ ${SOURCE_DIR} -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip index 7b8d2e6dfd..5920f9e9e1 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip @@ -18,4 +18,4 @@ python \ -e /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/include \ -b /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/bin \ -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm index ae2ef2f7d6..54bb3e40bc 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm @@ -20,4 +20,4 @@ automake --add-missing autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib index 4e9fbe46b5..0b548baf83 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib @@ -16,4 +16,4 @@ cd ${SOURCE_DIR} ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 index eb39e405da..f82fb4fa88 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1026-db5 @@ -16,5 +16,4 @@ cd ${SOURCE_DIR}/build_unix ../dist/configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies --build $(uname -m)-unknown-linux-gnu --enable-cxx --enable-compat185 -# Can be compiled safely with j1. make -j1 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 index 4b6db9066c..d13d19e6cd 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 @@ -20,4 +20,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ${SOURCE_DIR} -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac index 186f2c2193..0455aab6ba 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac @@ -18,4 +18,4 @@ automake --add-missing autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses index 08242b8135..7638ed7532 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses @@ -14,4 +14,4 @@ cd ${SOURCE_DIR} ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -with-shared -make -j${JSK_UNITREE_COMPILE_PARALLEL:-4} install +make -j4 install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools index 62a5b3a116..c2447e8fd6 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1032-speech-tools @@ -30,7 +30,6 @@ export PROJECT_MAJOR_VERSION=2 ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -# Can be compiled safely with j1. make -j1 PULSEAUDIO=none cp lib/*.a /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/lib diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival index 4873594a46..8060ba8551 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1033-festival @@ -28,7 +28,6 @@ libtoolize autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -# Can be compiled safely with j1. EST="../speech_tools" make -j1 all cp src/main/festival src/main/festival_client bin/text2wave /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/bin/ From 5cf2dec70a38d291a8987f94a2a134f8f6e86920 Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 27 Jul 2022 14:49:09 +0900 Subject: [PATCH 16/77] Revert "[jsk_unitree_startup/cross] Set default value 4 to JSK_UNITREE_COMPILE_PARALLEL" This reverts commit ff8c07358cd87281b5926f04fb74facb06591f38. --- jsk_unitree_robot/cross/build_ros1_dependencies.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index e1fcc8b020..fd718e4b8e 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -4,7 +4,6 @@ TARGET_MACHINE="${TARGET_MACHINE:-arm64v8}" HOST_INSTALL_ROOT="${BASE_ROOT:-${PWD}}/"${TARGET_MACHINE}_System INSTALL_ROOT=System SOURCE_ROOT=${TARGET_MACHINE}_ws_ros1_dependencies_sources -JSK_UNITREE_COMPILE_PARALLEL="${JSK_UNITREE_COMPILE_PARALLEL:-4}" if [ -e "${SOURCE_ROOT}" ]; then echo "WARNING: Source directory is found ${SOURCE_ROOT}" 1>&2 From 6023c3614baab4b7080b06339884ba8febe66f62 Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 27 Jul 2022 14:54:41 +0900 Subject: [PATCH 17/77] [jsk_unitree_startup/cross] Passthrough MAKEFLAGS --- jsk_unitree_robot/cross/build_ros1.sh | 2 ++ jsk_unitree_robot/cross/build_ros1_dependencies.sh | 2 ++ .../cross/ros1_dependencies_build_scripts/1001-urdfdom-headers | 2 +- .../cross/ros1_dependencies_build_scripts/1002-urdfdom | 2 +- .../cross/ros1_dependencies_build_scripts/1003-assimp | 2 +- .../cross/ros1_dependencies_build_scripts/1004-colladadom | 2 +- .../cross/ros1_dependencies_build_scripts/1005-yaml-cpp | 2 +- .../cross/ros1_dependencies_build_scripts/1007-snappy | 2 +- .../cross/ros1_dependencies_build_scripts/1008-opencv | 2 +- .../cross/ros1_dependencies_build_scripts/1009-python-sip | 2 +- .../cross/ros1_dependencies_build_scripts/1024-liblcm | 2 +- .../cross/ros1_dependencies_build_scripts/1025-alsa-lib | 2 +- .../1027-jack-audio-connection-kit | 2 +- .../cross/ros1_dependencies_build_scripts/1028-libportaudio2 | 2 +- .../cross/ros1_dependencies_build_scripts/1030-flac | 2 +- .../cross/ros1_dependencies_build_scripts/1031-libncurses | 2 +- 16 files changed, 18 insertions(+), 14 deletions(-) diff --git a/jsk_unitree_robot/cross/build_ros1.sh b/jsk_unitree_robot/cross/build_ros1.sh index 008db39f2a..2a5ef09574 100755 --- a/jsk_unitree_robot/cross/build_ros1.sh +++ b/jsk_unitree_robot/cross/build_ros1.sh @@ -4,6 +4,7 @@ TARGET_MACHINE="${TARGET_MACHINE:-arm64v8}" HOST_INSTALL_ROOT="${BASE_ROOT:-${PWD}}/"${TARGET_MACHINE}_System INSTALL_ROOT=System SOURCE_ROOT=${TARGET_MACHINE}_ws_system +MAKEFLAGS=${MAKEFLAGS:-'-j4'} UPDATE_SOURCE_ROOT=1 # TRUE if [ -e "${SOURCE_ROOT}" ]; then @@ -48,6 +49,7 @@ PR2EUS="pr2eus" docker run -it --rm \ -u $(id -u $USER) \ -e INSTALL_ROOT=${INSTALL_ROOT} \ + -e MAKEFLAGS=${MAKEFLAGS} \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies:ro \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies_setup.bash:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies_setup.bash:ro \ -v ${PWD}/startup_scripts/sitecustomize.py:/usr/lib/python2.7/sitecustomize.py:ro \ diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index fd718e4b8e..e194ad4155 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -4,6 +4,7 @@ TARGET_MACHINE="${TARGET_MACHINE:-arm64v8}" HOST_INSTALL_ROOT="${BASE_ROOT:-${PWD}}/"${TARGET_MACHINE}_System INSTALL_ROOT=System SOURCE_ROOT=${TARGET_MACHINE}_ws_ros1_dependencies_sources +MAKEFLAGS=${MAKEFLAGS:-'-j4'} if [ -e "${SOURCE_ROOT}" ]; then echo "WARNING: Source directory is found ${SOURCE_ROOT}" 1>&2 @@ -29,6 +30,7 @@ cp repos/go1_requirements_python3.txt ${SOURCE_ROOT}/go1_requirements_python3.tx docker run -it --rm \ -u $(id -u $USER) \ -e INSTALL_ROOT=${INSTALL_ROOT} \ + -e MAKEFLAGS=${MAKEFLAGS} \ -v ${PWD}/ros1_dependencies_build_scripts:/home/user/ros1_dependencies_build_scripts:ro \ -v ${PWD}/${SOURCE_ROOT}:/home/user/ros1_dependencies_sources:rw \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk//${INSTALL_ROOT}/ros1_dependencies:rw \ diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers index f9e2b9e3e0..5c43882e1c 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1001-urdfdom-headers @@ -10,4 +10,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ../../src/urdfdom-headers/urdfdom_headers-1.0.0 -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom index e4b8ea0d15..30144b316e 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1002-urdfdom @@ -10,4 +10,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ../../src/urdfdom/urdfdom-1.0.0 -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp index 3dc23b4619..acd3d11160 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1003-assimp @@ -23,4 +23,4 @@ cmake \ -DASSIMP_ENABLE_BOOST_WORKAROUND=OFF \ ${SOURCE_DIR} -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom index a74c303a62..152fb4da51 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1004-colladadom @@ -27,4 +27,4 @@ cmake \ -DOPT_DOUBLE_PRECISION=ON \ ${SOURCE_DIR} -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp index 0f1bcd80a0..1fc3ddf698 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1005-yaml-cpp @@ -19,4 +19,4 @@ cmake \ -DYAML_CPP_BUILD_TOOLS=ON \ ${SOURCE_DIR} -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy index a2a46b22b8..fb065a0a23 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1007-snappy @@ -18,4 +18,4 @@ cmake \ -DBUILD_SHARED_LIBS=on \ ${SOURCE_DIR} -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv index 4f52260bf5..adccf09285 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1008-opencv @@ -50,4 +50,4 @@ cmake \ -DOPENCV_CONFIG_INSTALL_PATH=lib/cmake/OpenCV \ ${SOURCE_DIR} -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip index 5920f9e9e1..dbeb077680 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1009-python-sip @@ -18,4 +18,4 @@ python \ -e /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/include \ -b /opt/jsk/${INSTALL_ROOT}/ros1_dependencies/bin \ -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm index 54bb3e40bc..20022e923b 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1024-liblcm @@ -20,4 +20,4 @@ automake --add-missing autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib index 0b548baf83..32bc8248f3 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1025-alsa-lib @@ -16,4 +16,4 @@ cd ${SOURCE_DIR} ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1027-jack-audio-connection-kit b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1027-jack-audio-connection-kit index a39053b965..57e32c0585 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1027-jack-audio-connection-kit +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1027-jack-audio-connection-kit @@ -17,4 +17,4 @@ cd ${SOURCE_DIR} ./autogen.sh CFLAGS="-I/opt/jsk/${INSTALL_ROOT}/ros1_dependencies/include" LIBS="-L/opt/jsk/${INSTALL_ROOT}/ros1_dependencies/lib" ./configure --enable-force-install --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 index d13d19e6cd..9c6c607838 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1028-libportaudio2 @@ -20,4 +20,4 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ ${SOURCE_DIR} -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac index 0455aab6ba..5de19e6a74 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1030-flac @@ -18,4 +18,4 @@ automake --add-missing autoconf ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -make -j4 install +make install diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses index 7638ed7532..bdcab81e05 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1031-libncurses @@ -14,4 +14,4 @@ cd ${SOURCE_DIR} ./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies -with-shared -make -j4 install +make install From b9bbd112a2624234696b043271c390f5bdd9448f Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 27 Jul 2022 16:36:04 +0900 Subject: [PATCH 18/77] [jsk_unitree_startup/cross] Add comments for unitree.repos --- jsk_unitree_robot/cross/repos/unitree.repos | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index b5c40d7b01..2bbcd8887a 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -21,11 +21,20 @@ repositories: type: git url: https://github.com/Affonso-Gui/app_manager version: enable-parallel-run-apps + # For parallel feature of app manager + # https://github.com/PR2/app_manager/pull/59 google_chat_ros: type: git url: https://github.com/iory/jsk_3rdparty version: google_chat_ros + # Support Google Chat API. + # https://github.com/jsk-ros-pkg/jsk_3rdparty/pull/323 + # To support aarch64, used the following branch. https://github.com/mqcmd196/jsk_3rdparty/pull/2 catkin_virtualenv: type: git url: https://github.com/iory/catkin_virtualenv version: avoid-catkin_run_tests_target + # At current, if we do a catkin build with CATKIN_ENABLE_TESTING=FALSE, + # we get the error 'Unknown CMake command "catkin_run_tests_target"' + # when calling catkin_generate_virtualenv. The following PR fixes this error. + # https://github.com/locusrobotics/catkin_virtualenv/pull/89 From e753fba8daa52de2e4334f0aa6d68ce29f03c729 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 28 Jul 2022 23:15:39 +0900 Subject: [PATCH 19/77] [jsk_unitree_startup/cross] Passthrough python libraries on run_user.sh --- jsk_unitree_robot/cross/run_user.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jsk_unitree_robot/cross/run_user.sh b/jsk_unitree_robot/cross/run_user.sh index 07c9f39652..b0b2bf57b5 100755 --- a/jsk_unitree_robot/cross/run_user.sh +++ b/jsk_unitree_robot/cross/run_user.sh @@ -17,6 +17,8 @@ docker run -it --rm \ -v ${HOST_INSTALL_ROOT}/ros1_inst:/opt/jsk/${INSTALL_ROOT}/ros1_inst:ro \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies_setup.bash:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies_setup.bash:ro \ -v ${HOST_INSTALL_ROOT}/system_setup.bash:/opt/jsk/${INSTALL_ROOT}/system_setup.bash:ro \ + -v ${HOST_INSTALL_ROOT}/sitecustomize.py:/usr/lib/python2.7/sitecustomize.py:ro \ + -v ${HOST_INSTALL_ROOT}/sitecustomize.py:/usr/lib/python3.6/sitecustomize.py:ro \ -v ${PWD}/${SOURCE_ROOT}:/opt/jsk/User:rw \ ros1-unitree:${TARGET_MACHINE} \ bash -c "echo 'source /opt/jsk/User/user_setup.bash; env; cd /opt/jsk/User' > ~/.bashrc; exec \"\$0\"" From ff338cf35b7f762d99e0d2c27fea8e0ce62f2071 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Fri, 29 Jul 2022 00:08:15 +0900 Subject: [PATCH 20/77] [unitree/cross] Add prerequisite command to readme --- jsk_unitree_robot/cross/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/cross/README.md b/jsk_unitree_robot/cross/README.md index 451e5766ff..a15b6f05c4 100644 --- a/jsk_unitree_robot/cross/README.md +++ b/jsk_unitree_robot/cross/README.md @@ -14,9 +14,9 @@ $ sudo usermod -aG docker $USER $ newgrp ``` -2. Install Qemu software +2. Install Qemu software and other prerequisites ``` -$ sudo apt install -y qemu-user-static +$ sudo apt install -y qemu-user-static sshpass python-vcstool ``` ### Build ROS System on Docker (Run only the fist time per host computer) From 33cf239cd1f835dbffb6963ab0425bc362f46700 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 29 Jul 2022 14:04:06 +0900 Subject: [PATCH 21/77] [jsk_unitree_startup/cross] Modified google_chat_ros branch --- jsk_unitree_robot/cross/repos/unitree.repos | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index 2bbcd8887a..8acfa235f2 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -25,11 +25,10 @@ repositories: # https://github.com/PR2/app_manager/pull/59 google_chat_ros: type: git - url: https://github.com/iory/jsk_3rdparty + url: https://github.com/mqcmd196/jsk_3rdparty version: google_chat_ros # Support Google Chat API. # https://github.com/jsk-ros-pkg/jsk_3rdparty/pull/323 - # To support aarch64, used the following branch. https://github.com/mqcmd196/jsk_3rdparty/pull/2 catkin_virtualenv: type: git url: https://github.com/iory/catkin_virtualenv From 3e1247f9949066dff3a0fff33210a613b2eafe00 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 29 Jul 2022 14:06:06 +0900 Subject: [PATCH 22/77] [jsk_unitree_startup/cross] Add comment for reachability --- jsk_unitree_robot/cross/install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index 6b8d5df4bd..fdebc5868c 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -54,6 +54,8 @@ function copy_data () { fi # Check if robot is reachable + # Use perl instead of `grep -Po '[0-9]{1,3}(?=% packet loss)'` for Mac environment. + # See https://stackoverflow.com/questions/16658333/grep-p-no-longer-works-how-can-i-rewrite-my-searches/16658690#16658690 reachability=$(ping -c4 ${hostname} 2>/dev/null | awk '/---/,0' | perl -nle'print $& while m{[0-9]{1,3}(?=% packet loss)}g') if [ -z "$reachability" ] || [ "$reachability" == 100 ]; then echo "ERROR: ${hostname} unreachable" 1>&2 From d6c223f77cac452d842393951fc904078ab73d13 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 29 Jul 2022 14:17:07 +0900 Subject: [PATCH 23/77] Revert "[jsk_unitree_startup/cross] Add python3-venv for catkin virtualenv" This reverts commit d62d039b2a72497bd4c7126159a959f093c6796e. --- jsk_unitree_robot/cross/docker/deb-packages.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/jsk_unitree_robot/cross/docker/deb-packages.txt b/jsk_unitree_robot/cross/docker/deb-packages.txt index f4b65059c9..84684bfd7c 100644 --- a/jsk_unitree_robot/cross/docker/deb-packages.txt +++ b/jsk_unitree_robot/cross/docker/deb-packages.txt @@ -1125,7 +1125,6 @@ python3-setuptools python3-simplejson python3-six python3-urllib3 -python3-venv python3-webencodings python3-wheel python3-xdg From 4b5dd183c8de170c3f278d27749903e825316935 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 29 Jul 2022 14:18:49 +0900 Subject: [PATCH 24/77] [jsk_unitree_startup/cross] Enable virtualenv via pip install --- jsk_unitree_robot/cross/repos/go1_requirements_python3.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt b/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt index 3f9f5d0b12..4e9c52a6c8 100644 --- a/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt +++ b/jsk_unitree_robot/cross/repos/go1_requirements_python3.txt @@ -1,2 +1,5 @@ # needeed by catkin_virtualenv +backports_abc catkin-pkg +singledispatch +virtualenv From 706a5b21f8fb41c4d8bac0b42bf73e9fec5905e3 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 29 Jul 2022 17:02:21 +0900 Subject: [PATCH 25/77] [jsk_unitree_startup/cross] Use unitree branch at catkin_virtualenv --- jsk_unitree_robot/cross/repos/unitree.repos | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index 8acfa235f2..5d4d73ae19 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -32,8 +32,13 @@ repositories: catkin_virtualenv: type: git url: https://github.com/iory/catkin_virtualenv - version: avoid-catkin_run_tests_target + version: unitree # At current, if we do a catkin build with CATKIN_ENABLE_TESTING=FALSE, # we get the error 'Unknown CMake command "catkin_run_tests_target"' # when calling catkin_generate_virtualenv. The following PR fixes this error. # https://github.com/locusrobotics/catkin_virtualenv/pull/89 + # + # Some python environments may not have ensurepip installed. + # Also, some users may not be able to use sudo apt install to install python3-venv (sudo command), etc. + # The following PR will enable catkin_virtualenv in environments without ensurepip by doing get-pip.py within venv. + # https://github.com/locusrobotics/catkin_virtualenv/pull/90 From e578bb82daef8b8de0bb023f18f8e6ca1ac8ce99 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 30 Jul 2022 17:08:30 +0900 Subject: [PATCH 26/77] [jsk_unitree_startup/cross] Install sysctl.conf to passthrough packet --- jsk_unitree_robot/cross/install.sh | 1 + .../jsk_unitree_startup/config/sysctl.conf | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/config/sysctl.conf diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index fdebc5868c..d99d078d5c 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -119,6 +119,7 @@ function copy_data () { # enable Internet with USB LTE module if [[ "${hostname}" == "192.168.123.161" ]]; then sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" + sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/sysctl.conf /etc/sysctl.conf" sshpass -p $PASS ssh -t ${user}@${hostname} "sudo systemctl restart dhcpcd" fi fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/config/sysctl.conf b/jsk_unitree_robot/jsk_unitree_startup/config/sysctl.conf new file mode 100644 index 0000000000..43006774dc --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/config/sysctl.conf @@ -0,0 +1,69 @@ +# +# /etc/sysctl.conf - Configuration file for setting system variables +# See /etc/sysctl.d/ for additional system variables. +# See sysctl.conf (5) for information. +# + +#kernel.domainname = example.com + +# Uncomment the following to stop low-level messages on console +#kernel.printk = 3 4 1 3 + +##############################################################3 +# Functions previously found in netbase +# + +# Uncomment the next two lines to enable Spoof protection (reverse-path filter) +# Turn on Source Address Verification in all interfaces to +# prevent some spoofing attacks +#net.ipv4.conf.default.rp_filter=1 +#net.ipv4.conf.all.rp_filter=1 + +# Uncomment the next line to enable TCP/IP SYN cookies +# See http://lwn.net/Articles/277146/ +# Note: This may impact IPv6 TCP sessions too +#net.ipv4.tcp_syncookies=1 + +# Uncomment the next line to enable packet forwarding for IPv4 +# Required to transfer packat to jetson. +net.ipv4.ip_forward=1 + +# Uncomment the next line to enable packet forwarding for IPv6 +# Enabling this option disables Stateless Address Autoconfiguration +# based on Router Advertisements for this host +#net.ipv6.conf.all.forwarding=1 + + +################################################################### +# Additional settings - these settings can improve the network +# security of the host and prevent against some network attacks +# including spoofing attacks and man in the middle attacks through +# redirection. Some network environments, however, require that these +# settings are disabled so review and enable them as needed. +# +# Do not accept ICMP redirects (prevent MITM attacks) +#net.ipv4.conf.all.accept_redirects = 0 +#net.ipv6.conf.all.accept_redirects = 0 +# _or_ +# Accept ICMP redirects only for gateways listed in our default +# gateway list (enabled by default) +# net.ipv4.conf.all.secure_redirects = 1 +# +# Do not send ICMP redirects (we are not a router) +#net.ipv4.conf.all.send_redirects = 0 +# +# Do not accept IP source route packets (we are not a router) +#net.ipv4.conf.all.accept_source_route = 0 +#net.ipv6.conf.all.accept_source_route = 0 +# +# Log Martian Packets +#net.ipv4.conf.all.log_martians = 1 +# + +################################################################### +# Magic system request Key +# 0=disable, 1=enable all, >1 bitmask of sysrq functions +# See https://www.kernel.org/doc/html/latest/admin-guide/sysrq.html +# for what other values do +#kernel.sysrq=438 + From 82acc9f0876c265b6a93093ca1bc6abe823159df Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 30 Jul 2022 17:17:10 +0900 Subject: [PATCH 27/77] [jsk_unitree_startup/cross] Install iptables.ipv4.nat to passthrough packets via LTE and wlan2 --- jsk_unitree_robot/cross/install.sh | 1 + .../config/iptables.ipv4.nat | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/config/iptables.ipv4.nat diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index d99d078d5c..4ca599d583 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -120,6 +120,7 @@ function copy_data () { if [[ "${hostname}" == "192.168.123.161" ]]; then sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/sysctl.conf /etc/sysctl.conf" + sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/iptables.ipv4.nat /etc/iptables.ipv4.nat" sshpass -p $PASS ssh -t ${user}@${hostname} "sudo systemctl restart dhcpcd" fi fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/config/iptables.ipv4.nat b/jsk_unitree_robot/jsk_unitree_startup/config/iptables.ipv4.nat new file mode 100644 index 0000000000..fdfa466fda --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/config/iptables.ipv4.nat @@ -0,0 +1,38 @@ +# Generated by xtables-save v1.8.2 on Tue Apr 26 15:11:12 2022 +*filter +:INPUT ACCEPT [248560:31230459] +:FORWARD ACCEPT [107:13046] +:OUTPUT ACCEPT [979900:329384767] +-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT +# Passthrough packets via LTE module. +-A FORWARD -i usb0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -o usb0 -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -i usb0 -o wlan1 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -i wlan1 -o usb0 -m state --state RELATED,ESTABLISHED -j ACCEPT +# Passthrough packets via wlan2 (for sparky) +-A FORWARD -i wlan2 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -o wlan2 -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -i wlan2 -o wlan1 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -i wlan1 -o wlan2 -m state --state RELATED,ESTABLISHED -j ACCEPT +COMMIT +# Completed on Tue Apr 26 15:11:12 2022 +# Generated by xtables-save v1.8.2 on Tue Apr 26 15:11:12 2022 +*nat +:PREROUTING ACCEPT [276:28555] +:INPUT ACCEPT [165:13454] +:POSTROUTING ACCEPT [355:25705] +:OUTPUT ACCEPT [414:29614] +-A POSTROUTING -o wlan2 -j MASQUERADE +-A POSTROUTING -o usb0 -j MASQUERADE +COMMIT +# Completed on Tue Apr 26 15:11:12 2022 +# Generated by xtables-save v1.8.2 on Tue Apr 26 15:11:12 2022 +*mangle +:PREROUTING ACCEPT [1265282:660359078] +:INPUT ACCEPT [1262670:659548895] +:FORWARD ACCEPT [2562:800616] +:OUTPUT ACCEPT [979900:329384767] +:POSTROUTING ACCEPT [982534:330195437] +COMMIT +# Completed on Tue Apr 26 15:11:12 2022 From 1a675157f17e94e408e1e7cbca5d2d7964f3c616 Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 3 Aug 2022 19:51:02 +0900 Subject: [PATCH 28/77] [jsk_unitree_startup/cross] Add OPTIONS to passthrough user id for mac compatibility --- jsk_unitree_robot/cross/build_ros1.sh | 15 +++++++++++++-- .../cross/build_ros1_dependencies.sh | 12 ++++++++++-- jsk_unitree_robot/cross/build_user.sh | 11 ++++++++++- jsk_unitree_robot/cross/run_user.sh | 11 ++++++++++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/jsk_unitree_robot/cross/build_ros1.sh b/jsk_unitree_robot/cross/build_ros1.sh index 2a5ef09574..31548745c5 100755 --- a/jsk_unitree_robot/cross/build_ros1.sh +++ b/jsk_unitree_robot/cross/build_ros1.sh @@ -33,7 +33,9 @@ mkdir -p ${HOST_INSTALL_ROOT}/ros1_inst if [ ${UPDATE_SOURCE_ROOT} -eq 1 ]; then vcs import --force --retry 3 --shallow ${SOURCE_ROOT}/src < repos/roseus_no_window.repos for dir in euslisp jskeus; do ls ${SOURCE_ROOT}/src/$dir/patches/; rsync -avz ${SOURCE_ROOT}/src/$dir/patches/ ${SOURCE_ROOT}/src/$dir; done - sed -i s@:{version}@0.0.0@ ${SOURCE_ROOT}/src/euslisp/package.xml ${SOURCE_ROOT}/src/jskeus/package.xml + # linux can use sed -i'.bak' and latest mac also supports same syntax. + # https://stackoverflow.com/questions/4247068/sed-command-with-i-option-failing-on-mac-but-works-on-linux/14813278#14813278 + sed -i.bak s@:{version}@0.0.0@ ${SOURCE_ROOT}/src/euslisp/package.xml ${SOURCE_ROOT}/src/jskeus/package.xml fi wget https://patch-diff.githubusercontent.com/raw/PR2/pr2_mechanism/pull/346.diff -O ${SOURCE_ROOT}/pr2_mechanism-346.diff @@ -46,8 +48,17 @@ JSK_ROBOT_UTILS="jsk_network_tools" DIAGNOSTIC_AGGREGATOR="diagnostic_aggregator" # jsk_XXX_startup usually depends on diagnostic_aggregator PR2EUS="pr2eus" +case ${OSTYPE} in + linux*) + OPTIONS="-u $(id -u $USER)" + ;; + darwin*) + OPTIONS="" + ;; +esac + docker run -it --rm \ - -u $(id -u $USER) \ + ${OPTIONS} \ -e INSTALL_ROOT=${INSTALL_ROOT} \ -e MAKEFLAGS=${MAKEFLAGS} \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies:ro \ diff --git a/jsk_unitree_robot/cross/build_ros1_dependencies.sh b/jsk_unitree_robot/cross/build_ros1_dependencies.sh index e194ad4155..9f51f7663c 100755 --- a/jsk_unitree_robot/cross/build_ros1_dependencies.sh +++ b/jsk_unitree_robot/cross/build_ros1_dependencies.sh @@ -27,8 +27,17 @@ mkdir -p ${HOST_INSTALL_ROOT}/Python cp repos/go1_requirements.txt ${SOURCE_ROOT}/go1_requirements.txt cp repos/go1_requirements_python3.txt ${SOURCE_ROOT}/go1_requirements_python3.txt +case ${OSTYPE} in + linux*) + OPTIONS="-u $(id -u $USER)" + ;; + darwin*) + OPTIONS="" + ;; +esac + docker run -it --rm \ - -u $(id -u $USER) \ + ${OPTIONS} \ -e INSTALL_ROOT=${INSTALL_ROOT} \ -e MAKEFLAGS=${MAKEFLAGS} \ -v ${PWD}/ros1_dependencies_build_scripts:/home/user/ros1_dependencies_build_scripts:ro \ @@ -45,7 +54,6 @@ docker run -it --rm \ done && \ pip3 install -U --user pip && \ pip install -U --user pip && \ - export PYTHONPATH=\"/opt/jsk/System/ros1_dependencies/lib/python2.7/site-packages\" && \ export PKG_CONFIG_PATH=\"/opt/jsk/${INSTALL_ROOT}/ros1_dependencies/lib/pkgconfig\" && \ ~/.local/bin/pip install --prefix=/opt/jsk/${INSTALL_ROOT}/Python -r /home/user/ros1_dependencies_sources/go1_requirements.txt && \ PYTHONPATH= ~/.local/bin/pip3 install --prefix=/opt/jsk/${INSTALL_ROOT}/Python -r /home/user/ros1_dependencies_sources/go1_requirements_python3.txt \ diff --git a/jsk_unitree_robot/cross/build_user.sh b/jsk_unitree_robot/cross/build_user.sh index d0855e30a1..9b3111f3d3 100755 --- a/jsk_unitree_robot/cross/build_user.sh +++ b/jsk_unitree_robot/cross/build_user.sh @@ -41,9 +41,18 @@ done # See https://github.com/k-okada/jsk_robot/issues/61 docker run -it --rm ros1-unitree:${TARGET_MACHINE} bash -c 'exit' || docker run --rm --privileged multiarch/qemu-user-static --reset -p yes +case ${OSTYPE} in + linux*) + OPTIONS="-u $(id -u $USER)" + ;; + darwin*) + OPTIONS="" + ;; +esac + # run on docker docker run -it --rm \ - -u $(id -u $USER) \ + ${OPTIONS} \ -e INSTALL_ROOT=${INSTALL_ROOT} \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies:ro \ -v ${HOST_INSTALL_ROOT}/Python:/opt/jsk/${INSTALL_ROOT}/Python:ro \ diff --git a/jsk_unitree_robot/cross/run_user.sh b/jsk_unitree_robot/cross/run_user.sh index b0b2bf57b5..22ca920773 100755 --- a/jsk_unitree_robot/cross/run_user.sh +++ b/jsk_unitree_robot/cross/run_user.sh @@ -7,10 +7,19 @@ SOURCE_ROOT=${TARGET_MACHINE}_User set -xeuf -o pipefail +case ${OSTYPE} in + linux*) + OPTIONS="-u $(id -u $USER)" + ;; + darwin*) + OPTIONS="" + ;; +esac + # -v ${PWD}/${TARGET_MACHINE}_ws_system:/home/user/${TARGET_MACHINE}_ws_system:rw \ # run on docker docker run -it --rm \ - -u $(id -u $USER) \ + ${OPTIONS} \ -e INSTALL_ROOT=${INSTALL_ROOT} \ -v ${HOST_INSTALL_ROOT}/ros1_dependencies:/opt/jsk/${INSTALL_ROOT}/ros1_dependencies:ro \ -v ${HOST_INSTALL_ROOT}/Python:/opt/jsk/${INSTALL_ROOT}/Python:ro \ From 91961fd9211dc0a6a823901b6e8f4942f2af8ed3 Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 3 Aug 2022 19:51:26 +0900 Subject: [PATCH 29/77] [jsk_unitree_startup/cross] Enable iory's google chat ros (minor change) --- jsk_unitree_robot/cross/repos/unitree.repos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/repos/unitree.repos b/jsk_unitree_robot/cross/repos/unitree.repos index 5d4d73ae19..12eb6185dc 100644 --- a/jsk_unitree_robot/cross/repos/unitree.repos +++ b/jsk_unitree_robot/cross/repos/unitree.repos @@ -25,7 +25,7 @@ repositories: # https://github.com/PR2/app_manager/pull/59 google_chat_ros: type: git - url: https://github.com/mqcmd196/jsk_3rdparty + url: https://github.com/iory/jsk_3rdparty version: google_chat_ros # Support Google Chat API. # https://github.com/jsk-ros-pkg/jsk_3rdparty/pull/323 From f7e60891d26ad773d22eb43ec93dd24639d4c973 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 4 Aug 2022 00:04:03 +0900 Subject: [PATCH 30/77] [jsk_unitree_startup/cross] Add compress command --- jsk_unitree_robot/cross/Makefile | 3 +++ jsk_unitree_robot/cross/compress.sh | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100755 jsk_unitree_robot/cross/compress.sh diff --git a/jsk_unitree_robot/cross/Makefile b/jsk_unitree_robot/cross/Makefile index e942246cf6..7a05f06953 100644 --- a/jsk_unitree_robot/cross/Makefile +++ b/jsk_unitree_robot/cross/Makefile @@ -20,6 +20,9 @@ user: install: ./install.sh +compress: + ./compress.sh + clean: rm -fr ${TARGET_MACHINE}_ws_* diff --git a/jsk_unitree_robot/cross/compress.sh b/jsk_unitree_robot/cross/compress.sh new file mode 100755 index 0000000000..57a097320e --- /dev/null +++ b/jsk_unitree_robot/cross/compress.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ -e "arm64v8_User" ]; then + if [ -e "arm64v8_User.tar.gz" ]; then + tar -zcvf arm64v8_User.tar.gz arm64v8_User + fi +fi + +if [ -e "arm64v8_System" ]; then + if [ -e "arm64v8_System.tar.gz" ]; then + chmod 644 arm64v8_System/ros1_inst/share/pr2eus/*.l + tar -zcvf arm64v8_System.tar.gz arm64v8_System + fi +fi From d1c49acd35f0eb05de5ced7b94238832b609e5df Mon Sep 17 00:00:00 2001 From: ichikura Date: Thu, 4 Aug 2022 21:26:24 +0900 Subject: [PATCH 31/77] [jsk_unitree_startup] Add emotion reaction --- .../emotion_reaction/emotion_reaction.app | 5 + .../emotion_reaction.interface | 2 + .../emotion_reaction/emotion_reaction.png | Bin 0 -> 23907 bytes .../emotion_reaction/emotion_reaction.xml | 4 + .../apps/unitree_apps.installed | 2 + .../autostart/jsk_startup.sh | 1 + .../launch/emotion_reaction.launch | 8 ++ .../launch/google_chat_ros.launch | 22 ++++ .../launch/unitree_bringup.launch | 6 + .../scripts/emotion_subscriber.l | 48 +++++++ .../scripts/emotion_talker.py | 117 ++++++++++++++++++ .../scripts/motions/affirmation.l | 46 +++++++ .../scripts/motions/astonished.l | 41 ++++++ .../scripts/motions/curious.l | 45 +++++++ .../scripts/motions/happy.l | 48 +++++++ .../jsk_unitree_startup/scripts/motions/joy.l | 51 ++++++++ .../scripts/motions/love.l | 64 ++++++++++ .../scripts/motions/negation.l | 61 +++++++++ .../scripts/motions/scared.l | 34 +++++ 19 files changed, 605 insertions(+) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.app create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.interface create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.png create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.xml create mode 100644 jsk_unitree_robot/jsk_unitree_startup/launch/emotion_reaction.launch create mode 100644 jsk_unitree_robot/jsk_unitree_startup/launch/google_chat_ros.launch create mode 100755 jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_subscriber.l create mode 100755 jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_talker.py create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/affirmation.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/astonished.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/curious.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/happy.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/joy.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/love.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/negation.l create mode 100644 jsk_unitree_robot/jsk_unitree_startup/scripts/motions/scared.l diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.app b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.app new file mode 100644 index 0000000000..2c59b16a4f --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.app @@ -0,0 +1,5 @@ +display: Emotion Reaction +platform: go1 +launch: jsk_unitree_startup/emotion_reaction.xml +interface: jsk_unitree_startup/emotion_reaction.interface +icon: jsk_unitree_startup/emotion_reaction.png diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.interface b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.interface new file mode 100644 index 0000000000..044105d644 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.interface @@ -0,0 +1,2 @@ +published_topics: {} +subscribed_topics: {} diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.png b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.png new file mode 100644 index 0000000000000000000000000000000000000000..f89099f76c4f0a7af8830f8545782335eba0bffe GIT binary patch literal 23907 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 W$LeQ*CG?8F9JqT&U!zJ58}>hb)jSsf literal 0 HcmV?d00001 diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.xml b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.xml new file mode 100644 index 0000000000..0c26c32e34 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/emotion_reaction/emotion_reaction.xml @@ -0,0 +1,4 @@ + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed b/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed index 6bafcd68ef..eb2d4caf45 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed @@ -3,3 +3,5 @@ apps: display: go1 watch dog - app: jsk_unitree_startup/lead_teleop display: go1 lead teleop + - app: jsk_unitree_startup/emotion_reaction + display: go1 emotion reaction diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh index 4f508dca41..63a7f350d1 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh +++ b/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh @@ -30,6 +30,7 @@ if [ "$ROS_IP" == "192.168.123.14" ];then while ! eval rostopic info /robotsound 2$toStartlog; do sleep 2; done sleep 2 # wait for a while... roslaunch jsk_unitree_startup unitree_bringup.launch network:=ethernet & + roslaunch jsk_unitree_startup google_chat_ros.launch & fi eval echo "[jsk_startup] done... " $toStartlog diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/emotion_reaction.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/emotion_reaction.launch new file mode 100644 index 0000000000..94515aaf26 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/emotion_reaction.launch @@ -0,0 +1,8 @@ + + + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/google_chat_ros.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/google_chat_ros.launch new file mode 100644 index 0000000000..d858a003f5 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/google_chat_ros.launch @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch index 7d06554a4d..4557b8eaee 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch @@ -39,6 +39,12 @@ args="call --wait /robot/start_app 'name: jsk_unitree_startup/lead_teleop'" output="screen" /> + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_subscriber.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_subscriber.l new file mode 100755 index 0000000000..80745c6254 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_subscriber.l @@ -0,0 +1,48 @@ +#!/usr/bin/env roseus + +(ros::load-ros-manifest "roseus") + +(ros::roseus "emotion-lister") +(load "package://unitreeeus/unitree-interface.l") + +(require "package://jsk_unitree_startup/scripts/motions/happy.l") +(require "package://jsk_unitree_startup/scripts/motions/joy.l") +(require "package://jsk_unitree_startup/scripts/motions/affirmation.l") +(require "package://jsk_unitree_startup/scripts/motions/negation.l") +(require "package://jsk_unitree_startup/scripts/motions/love.l") +(require "package://jsk_unitree_startup/scripts/motions/scared.l") +(require "package://jsk_unitree_startup/scripts/motions/curious.l") +(require "package://jsk_unitree_startup/scripts/motions/astonished.l") + + +(defun emotion-cb(msg) + (let ((emotion (send msg :data))) + (ros::ros-info "Callback chatting-cb called with ~A" emotion) + (cond ((string= emotion "happy") + (happy-main)) + ((string= emotion "joy") + (joy-main)) + ((string= emotion "affirmation") + (affirmation-main)) + ((string= emotion "negation") + (negation-main)) + ((string= emotion "love") + (love-main)) + ((string= emotion "scared") + (scared-main)) + ((string= emotion "curious") + (curious-main)) + ((string= emotion "astonished") + (astonished-main)) + (t + (ros::ros-warn "called unknown emotion ~A" emotion))) + emotion)) + +(defun main() + (go1-init) + (ros::subscribe "/emotion" std_msgs::String #'emotion-cb) + (ros::rate 10) + (while (ros::ok) + (ros::spin-once))) + +(main) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_talker.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_talker.py new file mode 100755 index 0000000000..73cc7b5422 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/emotion_talker.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import rospy +from std_msgs.msg import String +from speech_recognition_msgs.msg import SpeechRecognitionCandidates +from jsk_recognition_msgs.msg import PeoplePoseArray +from dialogflow_task_executive.msg import DialogResponse +import time +import message_filters + +from threading import Lock + + +class testNode(): + def __init__(self): + # Publisher + self.pub = rospy.Publisher('/emotion', String, queue_size=1) + + # Subscriber + self.sub1 = rospy.Subscriber('/speech_to_text', SpeechRecognitionCandidates, self.speech_callback) + self.sub2 = rospy.Subscriber('/people_pose', PeoplePoseArray, callback=self.callback) + self.sub3 = rospy.Subscriber('/dialog_response', DialogResponse, callback=self.df_cb) + self.duration_time = rospy.Duration(30) + self.prev_pose_detected_time = rospy.Time.now() - self.duration_time + + self.lock = Lock() + + queue_size = 10 + fps = 100. + delay = 1 / fps * 0.5 + + def callback(self, msg): + if (rospy.Time.now() - self.prev_pose_detected_time) < self.duration_time: + return + poses = msg.poses + if len(poses) >= 1: + item = poses[0].limb_names + scores = poses[0].scores + target_limbs = [ + "left_eye", "nose", "right_eye", "right_ear", + "left_ear"] + if all([name in item for name in target_limbs]): + message = "happy" + rospy.loginfo("received {}, current emotion is {}".format(item, message)) + with self.lock: + self.publish(message) + self.prev_pose_detected_time = rospy.Time.now() + rospy.sleep(5.0) + + def publish(self, data): + self.pub.publish(data) + + def speech_callback(self, msg): + word = msg.transcript[0] + if word in ["こんにちは", + "ヤッホー", + "こんばんは", + "おはよう", + "おはようございます"]: + message = "happy" + elif word in ["可愛いね", + "可愛い", + "かわいいね", + "かわいい"]: + message = "joy" + elif word in ["散歩に行こう", + "散歩", + "行こう", + "いこう"]: + message = "affirmation" + elif word in ["今日は行けないよ", + "いけないよ", + "行けないよ"]: + message = "negation" + elif word in ["大好きだよ", + "好き", + "すき"]: + message = "love" + elif word in ["あっち行って", + "さようなら"]: + message = "scared" + else: + message = word + rospy.loginfo("received {}, current emotion is {}".format(word, message)) + with self.lock: + self.publish(message) + rospy.sleep(5.0) + + def df_cb(self, data): + if data.action == "Happy" or data.action == "input.welcome": + self.publish("happy") + elif data.action == "Smirking" or data.action == "Squinting": + self.publish("joy") + elif data.action == "Love": + self.publish("love") + elif data.action == "Fearful" or data.action == "Cry": + self.publish("scared") + elif data.action == "Relived": + self.publish("affirmation") + elif data.action == "Boring" or data.action == "Unpleasant": + self.publish("negation") + elif data.action == "input.unknown": + self.publish("curious") + elif data.action == "Angry" or data.action == "Astonished": + self.publish("astonished") + else: + rospy.logwarn("Unknown emotion") + +if __name__ == '__main__': + rospy.init_node('speech_to_emotion') + + time.sleep(3.0) + node = testNode() + + while not rospy.is_shutdown(): + rospy.sleep(0.1) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/affirmation.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/affirmation.l new file mode 100644 index 0000000000..ac20ffd07c --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/affirmation.l @@ -0,0 +1,46 @@ +#!/usr/bin/env roseus \ + +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-affirmation") +;;(load "package://unitreeeus/unitree-interface.l") + +;;down +(defun affirmation-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0.2 0)) + )) +;;up +(defun affirmation-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 -0.2 0)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun affirmation-main() + (print "affirmation") + ;;(go1-init) + (reset-pose 1000) + (affirmation-pose-1 800) + (unix:usleep(* 1000 400)) + (affirmation-pose-2 800) + (unix:usleep(* 1000 400)) + (affirmation-pose-1 800) + (unix:usleep(* 1000 400)) + (affirmation-pose-2 800) + (unix:usleep(* 1000 600)) + + (reset-pose 1000) + (affirmation-pose-1 800) + (unix:usleep(* 1000 400)) + (affirmation-pose-2 800) + (unix:usleep(* 1000 400)) + (affirmation-pose-1 800) + (unix:usleep(* 1000 400)) + (affirmation-pose-2 800) + (unix:usleep(* 1000 600)) + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/astonished.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/astonished.l new file mode 100644 index 0000000000..c8d8ca80cb --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/astonished.l @@ -0,0 +1,41 @@ +#!/usr/bin/env roseus + +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-astonished") +;;(load "package://unitreeeus/unitree-interface.l") + +(defun astonished-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose (coords :pos #f(0 0 30))) + (send *ri* :body-pose (coords :pos #f(0 0 30))) + )) + +(defun astonished-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose '(0 -0.4 0)) + (send *ri* :body-pose '(0 -0.4 0)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun astonished-main() + ;;(print "astonished") + ;;(go1-init) + ;;(make-irtviewer) + ;;(send *irtviewer* :draw-objects) + ;;(objects (list *go1*)) + (reset-pose 1000) + (astonished-pose-1 800) + (unix:usleep (* 1000 1000)) + (reset-pose 800) + (unix:usleep (* 1000 400)) + (astonished-pose-2 800) + (unix:usleep (* 1000 1000)) + (reset-pose 800) + (unix:usleep (* 1000 800)) + + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/curious.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/curious.l new file mode 100644 index 0000000000..bd76eff6a7 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/curious.l @@ -0,0 +1,45 @@ +#!/usr/bin/env roseus +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-curious") +(load "package://unitreeeus/unitree-interface.l") + +(defun curious-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose '(0 -0.4 0)) + (send *ri* :body-pose '(0 -0.4 0)) + )) + +(defun curious-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose '(0.4 -0.4 0)) + (send *ri* :body-pose '(0.4 -0.4 0)) + )) + +(defun curious-pose-3(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose '(-0.4 -0.4 0)) + (send *ri* :body-pose '(-0.4 -0.4 0)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun curious-main() + (print "curious") + (go1-init) + ;;(make-irtviewer) + ;;(send *irtviewer* :draw-objects) + ;;(objects (list *go1*)) + (reset-pose 1000) + (curious-pose-1 800) + (unix:usleep (* 1000 800)) + (curious-pose-2 800) + (unix:usleep (* 1000 1500)) + (curious-pose-3 800) + (unix:usleep (* 1000 1500)) + (reset-pose 800) + (unix:usleep (* 1000 800)) + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/happy.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/happy.l new file mode 100644 index 0000000000..5dced3fdb2 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/happy.l @@ -0,0 +1,48 @@ +#!/usr/bin/env roseus +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-happy") +;;(load "package://unitreeeus/unitree-interface.l") + +(defun happy-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose '(0.4 -0.4 -0.2)) + (send *ri* :body-pose '(0.4 -0.4 -0.2)) + )) + +(defun happy-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :body-pose '(-0.4 -0.4 0.2)) + (send *ri* :body-pose '(-0.4 -0.4 0.2)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun happy-main() + (print "happy") + ;;(go1-init) + ;;(make-irtviewer) + ;;(send *irtviewer* :draw-objects) + ;;(objects (list *go1*)) + (reset-pose 1000) + (happy-pose-1 800) + (unix:usleep (* 1000 1000)) + (reset-pose 800) + (unix:usleep (* 1000 800)) + (happy-pose-2 800) + (unix:usleep (* 1000 1000)) + (reset-pose 800) + (unix:usleep (* 1000 800)) + + (happy-pose-1 800) + (unix:usleep (* 1000 1000)) + (reset-pose 800) + (unix:usleep (* 1000 800)) + (happy-pose-2 800) + (unix:usleep (* 1000 1000)) + (reset-pose 800) + (unix:usleep (* 1000 800)) + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/joy.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/joy.l new file mode 100644 index 0000000000..240c01d001 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/joy.l @@ -0,0 +1,51 @@ +#!/usr/bin/env roseus 1;5202;0c\ + +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-joy") +;;(load "package://unitreeeus/unitree-interface.l") + +;;down +(defun joy-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose (coords :pos #f(0 0 -90))) + )) +;;up +(defun joy-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose (coords :pos #f(0 0 30))) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun joy-main() + (print "joy") + ;;(go1-init) + (reset-pose 600) + (joy-pose-1 400) + (unix:usleep(* 1000 400)) + (joy-pose-2 800) + (unix:usleep(* 1000 400)) + (reset-pose 1000) + (unix:usleep(* 1000 400)) + (joy-pose-1 800) + (unix:usleep(* 1000 400)) + (joy-pose-2 800) + (unix:usleep(* 1000 600)) + + (reset-pose 1000) + (joy-pose-1 800) + (unix:usleep(* 1000 400)) + (joy-pose-2 800) + (unix:usleep(* 1000 400)) + (reset-pose) + (unix:usleep(* 1000 400)) + (joy-pose-1 800) + (unix:usleep(* 1000 400)) + (joy-pose-2 800) + (unix:usleep(* 1000 600)) + + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/love.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/love.l new file mode 100644 index 0000000000..648af83202 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/love.l @@ -0,0 +1,64 @@ +#!/usr/bin/env roseus \ + +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-love") +;;(load "package://unitreeeus/unitree-interface.l") + + +(defun love-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0.4 -0.2)) + )) + +(defun love-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0 -0.2)) + )) + +(defun love-pose-3(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0.4 0.2)) + )) + +(defun love-pose-4(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0 0.2)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun love-main() + (print "love") + ;;(go1-init) + (reset-pose 1000) + (unix:usleep(* 1000 600)) + (love-pose-1 800) + (unix:usleep(* 1000 400)) + (love-pose-2 800) + (unix:usleep(* 1000 400)) + (reset-pose) + (unix:usleep(* 1000 600)) + (love-pose-1 800) + (unix:usleep(* 1000 400)) + (love-pose-2 800) + (unix:usleep(* 1000 400)) + (reset-pose) + (unix:usleep(* 1000 800)) + + (love-pose-3 800) + (unix:usleep(* 1000 400)) + (love-pose-4 800) + (unix:usleep(* 1000 400)) + (reset-pose) + (unix:usleep(* 1000 600)) + (love-pose-3 800) + (unix:usleep(* 1000 400)) + (love-pose-4 800) + (unix:usleep(* 1000 400)) + (reset-pose) + (unix:usleep(* 1000 800)) + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/negation.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/negation.l new file mode 100644 index 0000000000..88734a206e --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/negation.l @@ -0,0 +1,61 @@ +#!/usr/bin/env roseus \ + +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-negation") +;;(load "package://unitreeeus/unitree-interface.l") + +;;down +(defun negation-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0 -0.2)) + )) +;;up +(defun negation-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0 0.2)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun negation-main() + (print "negation") + ;;(go1-init) + (reset-pose 1000) + (negation-pose-1 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 200)) + (negation-pose-2 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 200)) + (negation-pose-1 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 200)) + (negation-pose-2 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 600)) + + (reset-pose 1000) + (negation-pose-1 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 200)) + (negation-pose-2 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 200)) + (negation-pose-1 800) + (unix:usleep(* 1000 200)) + (reset-pose) + (unix:usleep(* 1000 200)) + (negation-pose-2 800) + (reset-pose) + (unix:usleep(* 1000 600)) + ) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/scared.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/scared.l new file mode 100644 index 0000000000..3ee72bfdac --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/motions/scared.l @@ -0,0 +1,34 @@ +#!/usr/bin/env roseus \ + +(ros::load-ros-manifest "roseus") +(ros::roseus "unitree-scared") +;;(load "package://unitreeeus/unitree-interface.l") + + +(defun scared-pose-1(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose (coords :pos #f(0 0 -90))) + )) + +(defun scared-pose-2(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *ri* :body-pose '(0 0.3 -0.2)) + )) + +(defun reset-pose(&optional (time 3000)) + (progn (send *go1* :angle-vector (send *ri* :state :potentio-vector)) + (send *go1* :angle-vector #f(0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0 0.0 45.0 -90.0)) + (send *ri* :body-pose '(0 0 0)) + )) + +(defun scared-main() + (print "scared") + ;;(go1-init) + (reset-pose 1000) + (unix:usleep(* 1000 1000)) + (scared-pose-1 800) + (unix:usleep(* 1000 1000)) + (scared-pose-2 800) + (unix:usleep(* 1000 2000)) + (reset-pose) +) From 0231f70cb3b26166dee126961b7a2d5a12df99aa Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 5 Aug 2022 00:11:29 +0900 Subject: [PATCH 32/77] [jsk_unitree_startup/cross] Add y or n --- jsk_unitree_robot/cross/compress.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/jsk_unitree_robot/cross/compress.sh b/jsk_unitree_robot/cross/compress.sh index 57a097320e..fd8a79096b 100755 --- a/jsk_unitree_robot/cross/compress.sh +++ b/jsk_unitree_robot/cross/compress.sh @@ -2,12 +2,27 @@ if [ -e "arm64v8_User" ]; then if [ -e "arm64v8_User.tar.gz" ]; then + echo "WARNING: Compressed arm64v8_User.tar.gz is found." + read -p "WARNING: Are you sure to continue [y/N] ? " -n 1 -r + echo # (optional) move to a new line + if [[ $REPLY =~ ^[Yy]$ ]]; then + tar -zcvf arm64v8_User.tar.gz arm64v8_User + fi + else tar -zcvf arm64v8_User.tar.gz arm64v8_User fi fi if [ -e "arm64v8_System" ]; then if [ -e "arm64v8_System.tar.gz" ]; then + echo "WARNING: Compressed arm64v8_System.tar.gz is found." + read -p "WARNING: Are you sure to continue [y/N] ? " -n 1 -r + echo # (optional) move to a new line + if [[ $REPLY =~ ^[Yy]$ ]]; then + chmod 644 arm64v8_System/ros1_inst/share/pr2eus/*.l + tar -zcvf arm64v8_System.tar.gz arm64v8_System + fi + else chmod 644 arm64v8_System/ros1_inst/share/pr2eus/*.l tar -zcvf arm64v8_System.tar.gz arm64v8_System fi From f8e3778dbe0ae069009adc75ef77571720b73953 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 5 Aug 2022 15:52:54 +0900 Subject: [PATCH 33/77] [jsk_unitree_startup] Add imageai.sh which is running on default unitree pro --- .../jsk_unitree_startup/autostart/imageai.sh | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh new file mode 100755 index 0000000000..6401998441 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh @@ -0,0 +1,72 @@ +#!/bin/bash +Nano1="192.168.123.13" ## SecNanoRight(1-2) +Nano2="192.168.123.14" ## SecNanoLeft(3-4) +Nano3="192.168.123.15" ## MasterNano(5) +eval echo "[imageai] starting ... " $toStartlog + +mLRootPATH="/home/unitree/Unitree/autostart/imageai/" +updatePackagePATH="/home/unitree/mLComSystemFrame.tar.gz" + +echo -e "\e[1;32m**** 1. Check if the program needs to be updated ? ****\e[0m" +checkIFUpdate(){ + if [ -f "$updatePackagePATH" ];then + echo -e "\e[1;32m The update file exists, it will be updating soon ...\e[0m" + tar -zxvf $updatePackagePATH -C $mLRootPATH > /dev/null; sleep 1 + rm -rf $updatePackagePATH + echo -e "\e[1;32m Updating Success.\e[0m" + else + echo -e "\e[1;31m NO Need to update!\e[0m" + fi +} + +checkIFUpdate +# dd=$? + +#declare -i lossNano1=-1 +#declare -i lossNano2=-1 +#declare -i lossNano3=-1 + +#echo -e "\e[1;32m**** 2. Check if all Nano'IP Address is OK ? ****\e[0m" + +#until (( lossNano1 + lossNano2 + lossNano3 == 0 )) +#do +# lossNano1=`ping -c 2 -w 2 $Nano1 | grep loss | awk '{print $6}' | awk -F "%" '{print $1}'` +# echo $lossNano1 +# +# lossNano2=`ping -c 2 -w 2 $Nano2 | grep loss | awk '{print $6}' | awk -F "%" '{print $1}'` +# echo $lossNano2 +# +# lossNano3=`ping -c 2 -w 2 $Nano3 | grep loss | awk '{print $6}' | awk -F "%" '{print $1}'` +# echo $lossNano3 +# #sleep 10 +#done + +#echo " IP is Right!" + +## GET PC's Address +localIPAddr=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6| head -n 1 | awk '{print $2}'|tr -d "addr:"` +echo "Local Address: "$localIPAddr + +echo -e "\e[1;32m**** 2. Start Main Application ... ****\e[0m" +if [ $localIPAddr == $Nano3 ];then + echo " MasterNano" + ############### MasterNano doing things!!!! + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqMNConfig.yaml; exec bash" + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" + +elif [ $localIPAddr == $Nano2 ];then + echo " SecNanoLeft" + ############### SecNanoLeft(3-4) doing things!!!! + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNLConfig.yaml; exec bash" + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNLConfig.yaml 1; exec bash" + +elif [ $localIPAddr == $Nano1 ];then + echo " SecNanoRight" + ############### SecNanoRight(1-2) doing things!!!! + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml; exec bash" + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml 1; exec bash" +else + echo " Not Found $localIPAddr in IP-Clump!" +fi + +echo " EveryThing is done!" From 8d9eb4a462e34bd62732f70f49e5c2cf1d22f8bd Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 5 Aug 2022 15:54:34 +0900 Subject: [PATCH 34/77] [jsk_unitree_startup] Launch trt pose on jetson nano2gb (192.168.123.13) --- jsk_unitree_robot/cross/install.sh | 14 +++++++++++--- .../jsk_unitree_startup/autostart/imageai.sh | 3 ++- .../scripts/publish_human_pose.diff | 17 ++++++++++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index e05fa2f36f..a2d184c6ee 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -119,6 +119,7 @@ function copy_data () { if [[ ${TYPE} == "Pro" ]] ; then copy_data pi 192.168.123.161 + copy_data unitree 192.168.123.13 copy_data unitree 192.168.123.14 ## copy_data unitree 192.168.123.15 : Pro : No Space for auto start elif [[ ${TYPE} == "Air" ]] ; then @@ -129,8 +130,10 @@ else fi if [[ "${TARGET_DIRECTORY}" == "User" ]]; then + cuda_ip="192.168.123.13" if [[ ${TYPE} == "Pro" ]] ; then - cuda_ip="192.168.123.15" + sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/src/jsk_robot/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh unitree@192.168.123.13:/home/unitree/Unitree/autostart/imageai/imageai.sh + sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/src/jsk_robot/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh unitree@192.168.123.15:/home/unitree/Unitree/autostart/imageai/imageai.sh elif [[ ${TYPE} == "Air" ]] ; then cuda_ip="192.168.123.13" fi @@ -142,8 +145,13 @@ if [[ "${TARGET_DIRECTORY}" == "User" ]]; then sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/src/jsk_robot/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff unitree@${cuda_ip}:/tmp/publish_human_pose.diff sshpass -p 123 ssh -t unitree@${cuda_ip} bash -c 'ls; OUT="$(patch -p0 --backup --forward /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/pyScripts/live_human_pose.py < /tmp/publish_human_pose.diff | tee /dev/tty)" || echo "${OUT}" | grep "Skipping patch" -q || (echo "$OUT" && false);' - if [[ ${TYPE} == "Air" ]] ; then - sshpass -p 123 ssh -t unitree@${cuda_ip} "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/pyScripts/live_human_pose.py" + # launch live_human_pose.py on jetson nano (192.168.123.13) + sshpass -p 123 ssh -t unitree@${cuda_ip} "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/pyScripts/live_human_pose.py" + # replace 192.168.123.15 -> 192.168.123.13 because live_human_pose.py is running on jetson nano (192.168.123.13) + if [[ ${TYPE} == "Pro" ]] ; then + sshpass -p 123 ssh -t unitree@192.168.123.13 "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/config/mqSNNRConfig.yaml" + sshpass -p 123 ssh -t unitree@192.168.123.14 "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/config/mqSNNLConfig.yaml" + sshpass -p 123 ssh -t unitree@192.168.123.15 "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/config/mqMNConfig.yaml" fi fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh index 6401998441..6f98fd581b 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh +++ b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh @@ -52,7 +52,7 @@ if [ $localIPAddr == $Nano3 ];then echo " MasterNano" ############### MasterNano doing things!!!! gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqMNConfig.yaml; exec bash" - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" + # gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" elif [ $localIPAddr == $Nano2 ];then echo " SecNanoLeft" @@ -65,6 +65,7 @@ elif [ $localIPAddr == $Nano1 ];then ############### SecNanoRight(1-2) doing things!!!! gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml; exec bash" gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml 1; exec bash" + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" else echo " Not Found $localIPAddr in IP-Clump!" fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff b/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff index 960c221f2f..cfae1f90d7 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff @@ -1,6 +1,12 @@ --- live_human_pose.py 2022-05-26 00:17:04.634784206 +0900 +++ live_human_pose_jsk.py 2022-05-26 00:17:18.102931810 +0900 -@@ -169,6 +169,8 @@ +@@ -1,4 +1,5 @@ + #coding:utf-8 ++import base64 + import json + import trt_pose . coco + import trt_pose . models +@@ -169,6 +171,8 @@ if 39 - 39: o00ooo0 - II111iiii * OoO0O00 % o0oOOo0O0Ooo * II111iiii % II111iiii cv2 . circle ( src , ( i1I1iI , o0O ) , 8 , I1i1I1II , - 1 ) iI1Ii11111iIi . write ( src ) @@ -9,3 +15,12 @@ # cv2 . imshow ( "ai" , src ) # cv2 . waitKey ( 1 ) if 59 - 59: iIii1I11I1II1 + I1IiiI - o0oOOo0O0Ooo - I1IiiI + Oo / I1ii11iIi11i +@@ -199,6 +203,8 @@ + print ( "AI is working ...." , camIndex . value ) + Oo0oOOo , Oo0OoO00oOO0o = o0OOO [ camIndex . value - 1 ] . read ( ) + OOO00O = time . time ( ) ++ # add by iory 2022.8.6 ++ O0ii1ii1ii.publish("vision/front_camera", base64.b64encode(cv2.imencode('.jpg', Oo0OoO00oOO0o, [int(cv2.IMWRITE_JPEG_QUALITY), 90])[1]).decode('ascii')) + OOoOO0oo0ooO = cv2 . resize ( Oo0OoO00oOO0o , dsize = ( OooO0 , II11iiii1Ii ) , interpolation = cv2 . INTER_AREA ) + iI ( OOoOO0oo0ooO , Oo0OoO00oOO0o , OOO00O ) + if 98 - 98: I1II1 * I1II1 / I1II1 + O00ooOO From 2b7943ce320d2332f338a2419c00ec1164470b23 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 6 Aug 2022 01:47:38 +0900 Subject: [PATCH 35/77] [jsk_unitree_startup] Add camera image publisher --- .../autostart/jsk_startup.sh | 4 + .../launch/camera_image_publisher.launch | 9 ++ .../scripts/camera_image_publisher.py | 86 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/launch/camera_image_publisher.launch create mode 100755 jsk_unitree_robot/jsk_unitree_startup/scripts/camera_image_publisher.py diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh index 4f508dca41..0e96a8e217 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh +++ b/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh @@ -20,6 +20,10 @@ if [ "$ROS_IP" == "192.168.123.161" ];then roslaunch --screen respeaker_ros sample_respeaker.launch language:=ja-JP publish_tf:=false launch_soundplay:=false & fi +if [ "$ROS_IP" == "192.168.123.13" ];then + roslaunch jsk_unitree_startup camera_image_publisher.launch & +fi + if [ "$ROS_IP" == "192.168.123.14" ];then # 192.168.123.14 is force updated within install.sh for Go1 Air if [ "$ROS_IP" == "192.168.123.13" ];then diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/camera_image_publisher.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/camera_image_publisher.launch new file mode 100644 index 0000000000..942961102c --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/camera_image_publisher.launch @@ -0,0 +1,9 @@ + + + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/camera_image_publisher.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/camera_image_publisher.py new file mode 100755 index 0000000000..ca1d079204 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/camera_image_publisher.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +import base64 + +import cv2 +import rospy +import numpy as np +from sensor_msgs.msg import Image +from sensor_msgs.msg import CompressedImage +import cv_bridge + +from paho.mqtt import client as mqtt_client + + +def decode_image_cv2(b64encoded): + bin = b64encoded.split(",")[-1] + bin = base64.b64decode(bin) + bin = np.frombuffer(bin, np.uint8) + img = cv2.imdecode(bin, cv2.IMREAD_COLOR) + return img + + +class ImagePublisher(object): + broker = '192.168.123.161' + port = 1883 + topic = "vision/front_camera" + + def __init__(self): + self.bridge = cv_bridge.CvBridge() + self.encoding = rospy.get_param('~encoding', 'bgr8') + self.frame_id = rospy.get_param('~frame_id', 'camera') + self.pub = rospy.Publisher('~output', Image, queue_size=1) + self.pub_compressed = rospy.Publisher( + '{}/compressed'.format(rospy.resolve_name('~output')), + CompressedImage, queue_size=1) + + self.connect_mqtt() + self.subscribe() + self.client.loop_start() + + def connect_mqtt(self): + def on_connect(client, userdata, flags, rc): + if rc == 0: + print("Connected to MQTT Broker! ({}:{})".format(self.broker, self.port)) + else: + print("Failed to connect, return code %d\n", rc) + self.client = mqtt_client.Client(rospy.get_name()) + self.client.on_connect = on_connect + self.client.connect(self.broker, self.port) + return + + def subscribe(self): + def on_message(client, userdata, msg): + rospy.loginfo("Received `{}` topic".format(msg.topic)) + + if self.pub.get_num_connections() == 0 and self.pub_compressed.get_num_connections() == 0: + return + now = rospy.Time.now() + frame = decode_image_cv2(msg.payload.decode('ascii')) + if self.pub.get_num_connections() > 0: + img_msg = self.bridge.cv2_to_imgmsg( + frame, encoding=self.encoding) + img_msg.header.frame_id = self.frame_id + img_msg.header.stamp = now + self.pub.publish(img_msg) + if self.pub_compressed.get_num_connections() > 0: + compressed_msg = CompressedImage() + # compressed format is separated by ';'. + # https://github.com/ros-perception/image_transport_plugins/blob/f0afd122ed9a66ff3362dc7937e6d465e3c3ccf7/compressed_image_transport/src/compressed_publisher.cpp#L116-L128 + compressed_msg.format = '{}; {} compressed {}'.format( + self.encoding, 'jpg', 'bgr8') + compressed_msg.data = np.array( + cv2.imencode('.jpg', frame)[1]).tostring() + compressed_msg.header.frame_id = self.frame_id + compressed_msg.header.stamp = now + self.pub_compressed.publish(compressed_msg) + + self.client.subscribe(self.topic) + self.client.on_message = on_message + return + + +if __name__ == '__main__': + rospy.init_node('camera_image_publisher') + ImagePublisher() + rospy.spin() From c9f5b1ab4d3ca634fa95a4d3db98b7ce3b161e59 Mon Sep 17 00:00:00 2001 From: iory Date: Sun, 7 Aug 2022 19:19:41 +0900 Subject: [PATCH 36/77] [jsk_unitree_startup] Add walk_notifyer --- .../launch/walk_notifier.launch | 34 ++++++ .../scripts/location_node.py | 109 ++++++++++++++++++ .../scripts/walk_notifier.py | 87 ++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch create mode 100755 jsk_unitree_robot/jsk_unitree_startup/scripts/location_node.py create mode 100755 jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch new file mode 100644 index 0000000000..a3ecc85a2d --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch @@ -0,0 +1,34 @@ + + + + + + + + + + + + + network_interface: $(arg network_interface) + + + + + + + + + + notify_interval: $(arg notify_interval) + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/location_node.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/location_node.py new file mode 100755 index 0000000000..62601cfc6e --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/location_node.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +import subprocess +import re + +import requests +import rospy +from std_srvs.srv import Trigger +from std_srvs.srv import TriggerResponse + + +cell_number_re = re.compile(r"^Cell\s+(?P.+)\s+-\s+Address:\s(?P.+)$") +regexps = [ + re.compile(r"^ESSID:\"(?P.*)\"$"), + re.compile(r"^Protocol:(?P.+)$"), + re.compile(r"^Mode:(?P.+)$"), + re.compile(r"^Frequency:(?P[\d.]+) (?P.+) \(Channel (?P\d+)\)$"), + re.compile(r"^Encryption key:(?P.+)$"), + re.compile(r"^Quality=(?P\d+)/(?P\d+)\s+Signal level=(?P.+) d.+$"), + re.compile(r"^Signal level=(?P\d+)/(?P\d+).*$"), +] + +# Detect encryption type +wpa_re = re.compile(r"IE:\ WPA\ Version\ 1$") +wpa2_re = re.compile(r"IE:\ IEEE\ 802\.11i/WPA2\ Version\ 1$") + + +def iwlist_scan(interface='wlan0'): + """Runs the comnmand to scan the list of networks. + + Must run as super user. + Does not specify a particular device, so will scan all network devices. + """ + cmd = ["iwlist", interface, "scan"] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + points = proc.stdout.read().decode('utf-8') + return points + + +def parse_iwlist(content): + """Parses the response from the command "iwlist scan" + + """ + cells = [] + lines = content.split('\n') + for line in lines: + line = line.strip() + cellNumber = cell_number_re.search(line) + if cellNumber is not None: + cells.append(cellNumber.groupdict()) + continue + wpa = wpa_re.search(line) + if wpa is not None: + cells[-1].update({'encryption': 'wpa'}) + wpa2 = wpa2_re.search(line) + if wpa2 is not None: + cells[-1].update({'encryption': 'wpa2'}) + for expression in regexps: + result = expression.search(line) + if result is not None: + if 'encryption' in result.groupdict(): + if result.groupdict()['encryption'] == 'on': + cells[-1].update({'encryption': 'wep'}) + else: + cells[-1].update({'encryption': 'off'}) + else: + cells[-1].update(result.groupdict()) + continue + return cells + + +class LocationNode(object): + + def __init__(self): + self.network_interface = rospy.get_param('~network_interface', 'wlan0') + self.location_key = rospy.get_param('~location_key') + self.service = rospy.Service('~get_location', + Trigger, self.get_location) + + def get_location(self, req): + aps = parse_iwlist(iwlist_scan(self.network_interface)) + contents = [{"macAddress": str(ap['mac']), "age": 0} + for ap in aps] + headers = {'Content-type': 'application/json'} + params = {'key': self.location_key, + 'language': 'ja'} + data = '{"wifiAccessPoints": ' + '[{}]'.format(contents) + '}' + + response = requests.post('https://www.googleapis.com/geolocation/v1/geolocate', + params=params, headers=headers, data=data) + # {'location': {'lat': 35.7145647, 'lng': 139.766433}, 'accuracy': 19.612} + hoge = response.json() + + lat = hoge['location']['lat'] + lng = hoge['location']['lng'] + # lat = 35.715106109567415 + # lng = 139.77380123496505 + response = requests.get( + 'https://maps.googleapis.com/maps/api/geocode/json?latlng={},{}'.format(lat, lng), + headers=headers, params=params) + return TriggerResponse(success=True, + message=response.text) + + +if __name__ == '__main__': + rospy.init_node('location_node') + location_node = LocationNode() + rospy.spin() diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py new file mode 100755 index 0000000000..9f82697c7e --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +import os +import subprocess +import time +import re +import json +import tempfile + +import PIL.Image +import sensor_msgs.msg +from std_srvs.srv import Trigger +import rospy +import cv_bridge + + +def send_mail(place, robot_name, sender_address, receiver_address, attachment=None): + """ + Send mail with mailutils + """ + mail_title = u"{}、お散歩中です。".format(robot_name) + message = u"お散歩してます。\\n今は{}を歩いているよ。".format(place) + cmd = u"echo -e '{}'".format(message) + cmd += u" | /usr/bin/mail -s '{}' -r {} {}".format( + mail_title, sender_address, receiver_address) + if attachment is not None: + cmd += ' -A {}'.format(attachment) + rospy.logerr('Executing: {}'.format(cmd.encode('utf-8'))) + exit_code = subprocess.call(cmd.encode('utf-8'), shell=True) + + +class WalkNotifier(object): + + def __init__(self): + self.bridge = cv_bridge.CvBridge() + self.robot_name = rospy.get_param('~robot_name').strip() + self.sub = rospy.Subscriber('~input_image', + sensor_msgs.msg.Image, + callback=self.callback, + queue_size=1) + self.sender_address = rospy.get_param('~sender_address') + self.receiver_address = rospy.get_param('~receiver_address') + self.notify_interval = int(rospy.get_param('~notify_interval', 180)) + rospy.wait_for_service('~get_location') + self.wait_image() + + def callback(self, img_msg): + self.img = self.bridge.imgmsg_to_cv2(img_msg, desired_encoding='bgr8') + + def wait_image(self): + self.img = None + rate = rospy.Rate(10) + while not rospy.is_shutdown() and self.img is None: + rate.sleep() + rospy.loginfo('[WalkMail] waiting image') + + def get_place(self): + response = rospy.ServiceProxy('~get_location', Trigger)() + address = json.loads(response.message) + a = address['results'][0]['formatted_address'] + print_address = " ".join(a.split(' ')[1:]) + + if self.img is not None: + _, img_path = tempfile.mkstemp(suffix='.jpg') + PIL.Image.fromarray(self.img[..., ::-1]).save(img_path) + send_mail(print_address, self.robot_name, + self.sender_address, self.receiver_address, + img_path) + os.remove(img_path) + else: + send_mail(print_address, self.robot_name, + self.sender_address, self.receiver_address, + img_path) + + def run(self): + rate = rospy.Rate(10) + while not rospy.is_shutdown(): + rate.sleep() + notifier.get_place() + time.sleep(self.notify_interval) + + +if __name__ == '__main__': + rospy.init_node('walk_notifier') + notifier = WalkNotifier() + notifier.run() From d3d98cc87445d6b68f8dba868319a64cdb201a48 Mon Sep 17 00:00:00 2001 From: iory Date: Sun, 7 Aug 2022 20:59:44 +0900 Subject: [PATCH 37/77] [jsk_unitree_startup/cross] Add env.sh for machine tag --- jsk_unitree_robot/cross/build_user.sh | 1 + jsk_unitree_robot/cross/startup_scripts/env.sh | 4 ++++ 2 files changed, 5 insertions(+) create mode 100755 jsk_unitree_robot/cross/startup_scripts/env.sh diff --git a/jsk_unitree_robot/cross/build_user.sh b/jsk_unitree_robot/cross/build_user.sh index 53c53356bc..dc0988d23d 100755 --- a/jsk_unitree_robot/cross/build_user.sh +++ b/jsk_unitree_robot/cross/build_user.sh @@ -60,3 +60,4 @@ docker run -it --rm \ --cmake-args -DCATKIN_ENABLE_TESTING=FALSE \ " 2>&1 | tee ${TARGET_MACHINE}_build_user.log cp ${PWD}/startup_scripts/user_setup.bash ${SOURCE_ROOT}/ +cp ${PWD}/startup_scripts/env.sh ${SOURCE_ROOT}/ diff --git a/jsk_unitree_robot/cross/startup_scripts/env.sh b/jsk_unitree_robot/cross/startup_scripts/env.sh new file mode 100755 index 0000000000..d158436b47 --- /dev/null +++ b/jsk_unitree_robot/cross/startup_scripts/env.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +source /opt/jsk/User/user_setup.bash +exec "$@" From e84bafd8ba84f8d7670b2b458390370b80a6114e Mon Sep 17 00:00:00 2001 From: iory Date: Sun, 7 Aug 2022 21:18:15 +0900 Subject: [PATCH 38/77] [jsk_unitree_startup] Add walk_notifier application --- .../apps/unitree_apps.installed | 2 + .../apps/walk_notifier/walk_notifier.app | 5 +++ .../walk_notifier/walk_notifier.interface | 2 + .../apps/walk_notifier/walk_notifier.png | Bin 0 -> 84233 bytes .../apps/walk_notifier/walk_notifier.xml | 6 +++ .../autostart/jsk_startup.sh | 1 + .../launch/get_location.launch | 15 +++++++ .../launch/walk_notifier.launch | 18 +------- .../scripts/walk_notifier.py | 39 ++++++++++++------ 9 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.app create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.interface create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.png create mode 100644 jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.xml create mode 100644 jsk_unitree_robot/jsk_unitree_startup/launch/get_location.launch diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed b/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed index 6bafcd68ef..ad52081ce4 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/unitree_apps.installed @@ -3,3 +3,5 @@ apps: display: go1 watch dog - app: jsk_unitree_startup/lead_teleop display: go1 lead teleop + - app: jsk_unitree_startup/walk_notifier + display: go1 walk notifier diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.app b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.app new file mode 100644 index 0000000000..41bc3f5339 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.app @@ -0,0 +1,5 @@ +display: Walk Notifier +platform: go1 +launch: jsk_unitree_startup/walk_notifier.xml +interface: jsk_unitree_startup/walk_notifier.interface +icon: jsk_unitree_startup/walk_notifier.png diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.interface b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.interface new file mode 100644 index 0000000000..044105d644 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.interface @@ -0,0 +1,2 @@ +published_topics: {} +subscribed_topics: {} diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.png b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.png new file mode 100644 index 0000000000000000000000000000000000000000..702466a6fc59ff33b7e153b208419a69eb949b7a GIT binary patch literal 84233 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
  • J8^|t*Wg`-Ds8NMG_fuwoz|U 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

    HwMt)HM13`?p0UZ7muh9@J6QfZ(D1 zz*zCPds~e*D)#}x6S%`LmQKQeEqnTqkDTB#e98Wl7w}^2Y1%!=0>d__lV*oDmpQ6P z3eY~G1o7_{gzU#A;#Lq8u!EWmuUcnj?#~(S{{gh= ?7e?2p~RQVoEde`;zrp6*@ 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 literal 0 HcmV?d00001 diff --git a/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.xml b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.xml new file mode 100644 index 0000000000..8093ee49b8 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/apps/walk_notifier/walk_notifier.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh index 0e96a8e217..a028fba255 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh +++ b/jsk_unitree_robot/jsk_unitree_startup/autostart/jsk_startup.sh @@ -17,6 +17,7 @@ if [ "$ROS_IP" == "192.168.123.161" ];then roslaunch --screen sound_play soundplay_node.launch sound_play:=robotsound & roslaunch --screen jsk_unitree_startup rwt_app_chooser.launch & roslaunch --screen jsk_unitree_startup rosserial_node.launch & + roslaunch --screen jsk_unitree_startup get_location.launch & roslaunch --screen respeaker_ros sample_respeaker.launch language:=ja-JP publish_tf:=false launch_soundplay:=false & fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/get_location.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/get_location.launch new file mode 100644 index 0000000000..f77874b3c4 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/get_location.launch @@ -0,0 +1,15 @@ + + + + + + + + + network_interface: $(arg network_interface) + + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch index a3ecc85a2d..d18374c75d 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/walk_notifier.launch @@ -1,31 +1,15 @@ - - - - - - - - network_interface: $(arg network_interface) - - + output="screen" > - notify_interval: $(arg notify_interval) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py index 9f82697c7e..3eeb2d52f3 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py @@ -20,21 +20,29 @@ def send_mail(place, robot_name, sender_address, receiver_address, attachment=No Send mail with mailutils """ mail_title = u"{}、お散歩中です。".format(robot_name) - message = u"お散歩してます。\\n今は{}を歩いているよ。".format(place) - cmd = u"echo -e '{}'".format(message) - cmd += u" | /usr/bin/mail -s '{}' -r {} {}".format( + message = u"お散歩してます。" + if place is not None: + message += u"\n今は{}を歩いているよ。".format(place) + cmd = u"mail -s '{}' -r {} {}".format( mail_title, sender_address, receiver_address) if attachment is not None: cmd += ' -A {}'.format(attachment) - rospy.logerr('Executing: {}'.format(cmd.encode('utf-8'))) - exit_code = subprocess.call(cmd.encode('utf-8'), shell=True) + cmd = ['mail', '-s', mail_title.encode('utf-8'), + '-r', sender_address, + receiver_address] + rospy.loginfo('Executing: {}'.format(' '.join(cmd))) + process = subprocess.Popen(cmd, + stdin=subprocess.PIPE, + universal_newlines=True) + process.communicate(message.encode('utf-8')) + process.wait() class WalkNotifier(object): def __init__(self): self.bridge = cv_bridge.CvBridge() - self.robot_name = rospy.get_param('~robot_name').strip() + self.robot_name = rospy.get_param('~robot_name', 'unitree').strip() self.sub = rospy.Subscriber('~input_image', sensor_msgs.msg.Image, callback=self.callback, @@ -56,11 +64,14 @@ def wait_image(self): rospy.loginfo('[WalkMail] waiting image') def get_place(self): - response = rospy.ServiceProxy('~get_location', Trigger)() - address = json.loads(response.message) - a = address['results'][0]['formatted_address'] - print_address = " ".join(a.split(' ')[1:]) - + try: + response = rospy.ServiceProxy('~get_location', Trigger)() + address = json.loads(response.message) + a = address['results'][0]['formatted_address'] + print_address = " ".join(a.split(' ')[1:]) + except Exception as e: + rospy.logerr('Error: {}'.format(str(e))) + print_address = None if self.img is not None: _, img_path = tempfile.mkstemp(suffix='.jpg') PIL.Image.fromarray(self.img[..., ::-1]).save(img_path) @@ -77,7 +88,11 @@ def run(self): rate = rospy.Rate(10) while not rospy.is_shutdown(): rate.sleep() - notifier.get_place() + try: + notifier.get_place() + except Exception as e: + rospy.logerr('Error: {}'.format(str(e))) + continue time.sleep(self.notify_interval) From 11964cf990213b5b4be4b6d99e43eca6be450c60 Mon Sep 17 00:00:00 2001 From: iory Date: Sun, 7 Aug 2022 21:33:42 +0900 Subject: [PATCH 39/77] [jsk_unitree_startup] Add wlan2 settings for sparky --- .../jsk_unitree_startup/config/dhcpcd.conf | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/jsk_unitree_robot/jsk_unitree_startup/config/dhcpcd.conf b/jsk_unitree_robot/jsk_unitree_startup/config/dhcpcd.conf index c2d1530f29..77b0d64c07 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/config/dhcpcd.conf +++ b/jsk_unitree_robot/jsk_unitree_startup/config/dhcpcd.conf @@ -76,4 +76,13 @@ nohook wpa_supplicant interface usb0 metric 100 static domain_name_servers=8.8.8.8 -############################################### \ No newline at end of file +############################################### + +################################################ +# For sparky +# This enables Unitree(sparky) to connect the Internet. +################################################ +interface wlan2 +metric 101 +static domain_name_servers=8.8.8.8 +############################################### From 5813abd6fde89b6551b111a958aaf3174bae7cda Mon Sep 17 00:00:00 2001 From: iory Date: Sun, 7 Aug 2022 22:10:17 +0900 Subject: [PATCH 40/77] [jsk_unitree_startup/cross] Install mailutils --- .../cross/repos/ros1_dependencies.repos | 6 ++++++ .../1037-mailutils | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100755 jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils diff --git a/jsk_unitree_robot/cross/repos/ros1_dependencies.repos b/jsk_unitree_robot/cross/repos/ros1_dependencies.repos index 2983607027..a9ff0f4105 100644 --- a/jsk_unitree_robot/cross/repos/ros1_dependencies.repos +++ b/jsk_unitree_robot/cross/repos/ros1_dependencies.repos @@ -203,3 +203,9 @@ repositories: festlex-poslex: type: tar url: http://archive.ubuntu.com/ubuntu/pool/universe/f/festlex-poslex/festlex-poslex_2.4.orig.tar.gz + mailutils: + type: tar + url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.4.orig.tar.xz + mailutils/debian: + type: tar + url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.4-1.debian.tar.xz diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils new file mode 100755 index 0000000000..510b004c88 --- /dev/null +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils @@ -0,0 +1,19 @@ +#!/bin/bash +set -xeu -o pipefail + +DEBIAN_DIR=/home/user/ros1_dependencies_sources/src/mailutils/debian/debian +SOURCE_DIR=/home/user/ros1_dependencies_sources/src/mailutils/mailutils-3.4 + +# +# constantly does not have patches +# +cd ${DEBIAN_DIR}/patches +for patch_file in $(grep -v ^# series); do + OUT="$(patch -p1 --forward --directory ${SOURCE_DIR} < ${patch_file} | tee /dev/tty)" || echo "${OUT}" | grep "Skipping patch" -q || (echo "$OUT" && false) || echo "OK" +done + +cd ${SOURCE_DIR} + +./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies + +make install From 8758d438a9d1eac1c9fa6f3f70194da5e9eba2cf Mon Sep 17 00:00:00 2001 From: iory Date: Sun, 7 Aug 2022 23:24:15 +0900 Subject: [PATCH 41/77] [jsk_unitree_startup] Enable wlan0 --- jsk_unitree_robot/cross/install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index a2d184c6ee..349ec77720 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -113,7 +113,11 @@ function copy_data () { if [[ "${hostname}" == "192.168.123.161" ]]; then sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" sshpass -p $PASS ssh -t ${user}@${hostname} "sudo systemctl restart dhcpcd" + + # enable wlan0 + sshpass -p $PASS ssh -t ${user}@${hostname} "sed -i 's/sudo ifconfig wlan0 down/# sudo ifconfig wlan0 down/g' /home/pi/Unitree/autostart/configNetwork/configNetwork.sh" fi + set +x } From 6e47211ca589970ce05958408e707bb9f107e8f8 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 00:31:12 +0900 Subject: [PATCH 42/77] [jsk_unitree_startup/install] passthrough password --- jsk_unitree_robot/cross/install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index 349ec77720..542eec10e0 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -79,7 +79,7 @@ function copy_data () { echo "Check user id for /dev/ttyACM0 etc" echo "===" sshpass -p $PASS ssh -t ${user}@${hostname} bash -c 'id' - sshpass -p $PASS ssh -t ${user}@${hostname} bash -c "groups | grep dialout || (set -x; sudo usermod -a -G dialout ${user})" + sshpass -p $PASS ssh -t ${user}@${hostname} bash -c "groups | grep dialout || (set -x; echo $PASS | sudo -S usermod -a -G dialout ${user})" echo "===" # check if we have jsk_startup in .startlist @@ -90,7 +90,7 @@ function copy_data () { # check if we have /opt/jsk set -x sshpass -p $PASS ssh -t ${user}@${hostname} "test -e /opt/jsk" || \ - sshpass -p $PASS ssh -t ${user}@${hostname} "sudo mkdir -p /opt/jsk && sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /opt/jsk && ls -al /opt/jsk" + sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S mkdir -p /opt/jsk && sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /opt/jsk && ls -al /opt/jsk" rsync --rsh="/usr/bin/sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded --exclude "*.pyc" --exclude "^logs/" ${TARGET_MACHINE}_${TARGET_DIRECTORY}/ ${hostname}:/opt/jsk/${TARGET_DIRECTORY} if [[ "${TARGET_DIRECTORY}" == "User" ]]; then @@ -104,15 +104,15 @@ function copy_data () { # update udev # respeaker_ros - sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find respeaker_ros)/config/60-respeaker.rules /etc/udev/rules.d/60-respeaker.rules" + sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; echo $PASS | sudo -S cp -f \$(rospack find respeaker_ros)/config/60-respeaker.rules /etc/udev/rules.d/60-respeaker.rules" # - sshpass -p $PASS ssh -t ${user}@${hostname} "ls -al /etc/udev/rules.d/; sudo systemctl restart udev" + sshpass -p $PASS ssh -t ${user}@${hostname} "ls -al /etc/udev/rules.d/; echo $PASS | sudo -S systemctl restart udev" fi # enable Internet with USB LTE module if [[ "${hostname}" == "192.168.123.161" ]]; then - sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; sudo cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" - sshpass -p $PASS ssh -t ${user}@${hostname} "sudo systemctl restart dhcpcd" + sshpass -p $PASS ssh -t ${user}@${hostname} "source /opt/jsk/User/user_setup.bash; echo $PASS | sudo -S cp -f \$(rospack find jsk_unitree_startup)/config/dhcpcd.conf /etc/dhcpcd.conf" + sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S systemctl restart dhcpcd" # enable wlan0 sshpass -p $PASS ssh -t ${user}@${hostname} "sed -i 's/sudo ifconfig wlan0 down/# sudo ifconfig wlan0 down/g' /home/pi/Unitree/autostart/configNetwork/configNetwork.sh" From 6cf0090e4d07b376837b301f11976718bcfc3944 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 00:52:06 +0900 Subject: [PATCH 43/77] [jsk_unitree_startup/install] Modified owner of /var/mail/${USER} --- jsk_unitree_robot/cross/install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index 542eec10e0..6a3cf3468b 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -92,6 +92,10 @@ function copy_data () { sshpass -p $PASS ssh -t ${user}@${hostname} "test -e /opt/jsk" || \ sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S mkdir -p /opt/jsk && sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /opt/jsk && ls -al /opt/jsk" + # check if we have /var/mail/${user} + sshpass -p $PASS ssh -t ${user}@${hostname} "touch /var/mail/${user}" && \ + sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /var/mail/${user}" + rsync --rsh="/usr/bin/sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded --exclude "*.pyc" --exclude "^logs/" ${TARGET_MACHINE}_${TARGET_DIRECTORY}/ ${hostname}:/opt/jsk/${TARGET_DIRECTORY} if [[ "${TARGET_DIRECTORY}" == "User" ]]; then rsync --rsh="/usr/bin/sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded ../jsk_unitree_startup/autostart/ ${hostname}:Unitree/autostart/jsk_startup From c2cb48c485186c8e2b47f9007065b503e081964b Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 00:59:13 +0900 Subject: [PATCH 44/77] [jsk_unitree_startup/WalkNotifier] Enable attachment --- jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py index 3eeb2d52f3..68c6c3bce0 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/walk_notifier.py @@ -30,6 +30,8 @@ def send_mail(place, robot_name, sender_address, receiver_address, attachment=No cmd = ['mail', '-s', mail_title.encode('utf-8'), '-r', sender_address, receiver_address] + if attachment is not None: + cmd += ['-A', attachment] rospy.loginfo('Executing: {}'.format(' '.join(cmd))) process = subprocess.Popen(cmd, stdin=subprocess.PIPE, From 95fdfa54bf87d4016172f37446f0a75ee923779b Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 01:25:59 +0900 Subject: [PATCH 45/77] [jsk_unitree_startup] Update version of mailutils --- jsk_unitree_robot/cross/repos/ros1_dependencies.repos | 4 ++-- .../cross/ros1_dependencies_build_scripts/1037-mailutils | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jsk_unitree_robot/cross/repos/ros1_dependencies.repos b/jsk_unitree_robot/cross/repos/ros1_dependencies.repos index a9ff0f4105..9fddaa0137 100644 --- a/jsk_unitree_robot/cross/repos/ros1_dependencies.repos +++ b/jsk_unitree_robot/cross/repos/ros1_dependencies.repos @@ -205,7 +205,7 @@ repositories: url: http://archive.ubuntu.com/ubuntu/pool/universe/f/festlex-poslex/festlex-poslex_2.4.orig.tar.gz mailutils: type: tar - url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.4.orig.tar.xz + url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.7.orig.tar.xz mailutils/debian: type: tar - url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.4-1.debian.tar.xz + url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.7-2.1.debian.tar.xz diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils index 510b004c88..7c44f1dfa2 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils @@ -2,7 +2,7 @@ set -xeu -o pipefail DEBIAN_DIR=/home/user/ros1_dependencies_sources/src/mailutils/debian/debian -SOURCE_DIR=/home/user/ros1_dependencies_sources/src/mailutils/mailutils-3.4 +SOURCE_DIR=/home/user/ros1_dependencies_sources/src/mailutils/mailutils-3.7 # # constantly does not have patches From 15260f70a28609978983d32aa0e9427aad2d1902 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 03:01:29 +0900 Subject: [PATCH 46/77] [jsk_unitree_startup/cross] Disable python for mailutils --- .../cross/ros1_dependencies_build_scripts/1037-mailutils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils index 7c44f1dfa2..b4043d0cb6 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils @@ -14,6 +14,6 @@ done cd ${SOURCE_DIR} -./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies +./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies --disable-python make install From fca125f619f6d4e574b07b563c9e14075cbb42c9 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 10:52:19 +0900 Subject: [PATCH 47/77] [jsk_unitree_startup/install.sh] Use sudo for touch /var/mail/unitree --- jsk_unitree_robot/cross/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index ea78fabe9d..de8f2639e8 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -101,7 +101,7 @@ function copy_data () { sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S mkdir -p /opt/jsk && sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /opt/jsk && ls -al /opt/jsk" # check if we have /var/mail/${user} - sshpass -p $PASS ssh -t ${user}@${hostname} "touch /var/mail/${user}" && \ + sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S touch /var/mail/${user}" && \ sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S sudo chown -R \$(id -u \${USER}):\$(id -g \${USER}) /var/mail/${user}" rsync --rsh="sshpass -p $PASS ssh -o StrictHostKeyChecking=no -l ${user}" -avz --delete --delete-excluded --exclude "*.pyc" --exclude "^logs/" ${TARGET_MACHINE}_${TARGET_DIRECTORY}/ ${hostname}:/opt/jsk/${TARGET_DIRECTORY} From d541c813132712f3856f3e0e3de64ab4c50abe75 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 12:44:45 +0900 Subject: [PATCH 48/77] [jsk_unitree_startup] Add Walk notifier's README --- .../jsk_unitree_startup/README.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 jsk_unitree_robot/jsk_unitree_startup/README.md diff --git a/jsk_unitree_robot/jsk_unitree_startup/README.md b/jsk_unitree_robot/jsk_unitree_startup/README.md new file mode 100644 index 0000000000..39c1410bf4 --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/README.md @@ -0,0 +1,26 @@ +# jsk_unitree_startup + +## app_manager + +The unitree launches various apps via [app_manager](https://github.com/pr2/app_manager). + +You can launch the app from the browser by accessing this [url](http://192.168.123.161:8000/rwt_app_chooser/). + +### Walk Notifier + +Send an email with the location of the walk and a camera image of the walk. + +![](./apps/walk_notifier/walk_notifier.png) + +#### prerequirement + +The key for google map needs to be placed in `/var/lib/robot/google_map_location_key.yaml` (pi@192.168.123.161) with 'location_key' as the key. + +For JSK members, the yaml file are available at [Google Drive](https://drive.google.com/file/d/1D867WB70GDEN0g9IKXfoTHk-5MUJFiuf/view?usp=sharing) + +Also, place a yaml file with the email addresses of the sender and receiver in `/var/lib/robot/walk_notifier.yaml`. + +``` +sender_address: hogehoge@some.mail.com +receiver_address: foo@some.mail.com +``` From 722b3fbcda1d9e99f8b9b9cff3581cec2a9f5e0a Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 5 Aug 2022 00:11:29 +0900 Subject: [PATCH 49/77] [jsk_unitree_startup/cross] Add y or n --- jsk_unitree_robot/cross/compress.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/jsk_unitree_robot/cross/compress.sh b/jsk_unitree_robot/cross/compress.sh index 57a097320e..fd8a79096b 100755 --- a/jsk_unitree_robot/cross/compress.sh +++ b/jsk_unitree_robot/cross/compress.sh @@ -2,12 +2,27 @@ if [ -e "arm64v8_User" ]; then if [ -e "arm64v8_User.tar.gz" ]; then + echo "WARNING: Compressed arm64v8_User.tar.gz is found." + read -p "WARNING: Are you sure to continue [y/N] ? " -n 1 -r + echo # (optional) move to a new line + if [[ $REPLY =~ ^[Yy]$ ]]; then + tar -zcvf arm64v8_User.tar.gz arm64v8_User + fi + else tar -zcvf arm64v8_User.tar.gz arm64v8_User fi fi if [ -e "arm64v8_System" ]; then if [ -e "arm64v8_System.tar.gz" ]; then + echo "WARNING: Compressed arm64v8_System.tar.gz is found." + read -p "WARNING: Are you sure to continue [y/N] ? " -n 1 -r + echo # (optional) move to a new line + if [[ $REPLY =~ ^[Yy]$ ]]; then + chmod 644 arm64v8_System/ros1_inst/share/pr2eus/*.l + tar -zcvf arm64v8_System.tar.gz arm64v8_System + fi + else chmod 644 arm64v8_System/ros1_inst/share/pr2eus/*.l tar -zcvf arm64v8_System.tar.gz arm64v8_System fi From 749f4869dd015bd0e86eb33be7d8f6fc5047ee5f Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 20:09:00 +0900 Subject: [PATCH 50/77] [jsk_perception_3rdparty] Passthrough password --- jsk_unitree_robot/cross/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index de8f2639e8..767bb10b03 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -68,8 +68,8 @@ function copy_data () { if [[ "${TARGET_DIRECTORY}" == "System" ]]; then sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/sitecustomize.py ${user}@${hostname}:/tmp/sitecustomize.py - sshpass -p $PASS ssh -t ${user}@${hostname} "sudo cp -f /tmp/sitecustomize.py /usr/lib/python2.7/sitecustomize.py" - sshpass -p $PASS ssh -t ${user}@${hostname} "sudo cp -f /tmp/sitecustomize.py /usr/lib/python3/sitecustomize.py" + sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S cp -f /tmp/sitecustomize.py /usr/lib/python2.7/sitecustomize.py" + sshpass -p $PASS ssh -t ${user}@${hostname} "echo $PASS | sudo -S cp -f /tmp/sitecustomize.py /usr/lib/python3/sitecustomize.py" fi # cehck disk space From e1c68e5b9a16a5abd70ea7ee3388d3ada333f5a0 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 8 Aug 2022 23:24:20 +0900 Subject: [PATCH 51/77] [jsk_unitree_startup] Fixed start_app's rosservice names --- .../jsk_unitree_startup/launch/unitree_bringup.launch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch index 4557b8eaee..0534bb9072 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/unitree_bringup.launch @@ -34,13 +34,13 @@ - - From 14022cd2d8cf315c21cacc5394302a1d1fab5044 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 27 Aug 2022 18:37:15 +0900 Subject: [PATCH 52/77] Revert "[jsk_unitree_startup] Update version of mailutils" This reverts commit 95fdfa54bf87d4016172f37446f0a75ee923779b. --- jsk_unitree_robot/cross/repos/ros1_dependencies.repos | 4 ++-- .../cross/ros1_dependencies_build_scripts/1037-mailutils | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jsk_unitree_robot/cross/repos/ros1_dependencies.repos b/jsk_unitree_robot/cross/repos/ros1_dependencies.repos index 9fddaa0137..a9ff0f4105 100644 --- a/jsk_unitree_robot/cross/repos/ros1_dependencies.repos +++ b/jsk_unitree_robot/cross/repos/ros1_dependencies.repos @@ -205,7 +205,7 @@ repositories: url: http://archive.ubuntu.com/ubuntu/pool/universe/f/festlex-poslex/festlex-poslex_2.4.orig.tar.gz mailutils: type: tar - url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.7.orig.tar.xz + url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.4.orig.tar.xz mailutils/debian: type: tar - url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.7-2.1.debian.tar.xz + url: http://archive.ubuntu.com/ubuntu/pool/universe/m/mailutils/mailutils_3.4-1.debian.tar.xz diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils index b4043d0cb6..d95efeaefe 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils @@ -2,7 +2,7 @@ set -xeu -o pipefail DEBIAN_DIR=/home/user/ros1_dependencies_sources/src/mailutils/debian/debian -SOURCE_DIR=/home/user/ros1_dependencies_sources/src/mailutils/mailutils-3.7 +SOURCE_DIR=/home/user/ros1_dependencies_sources/src/mailutils/mailutils-3.4 # # constantly does not have patches From 69874b1f53f712ebcfeea76f1e85c720625e89b6 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 27 Aug 2022 18:37:19 +0900 Subject: [PATCH 53/77] Revert "[jsk_unitree_startup/cross] Disable python for mailutils" This reverts commit 15260f70a28609978983d32aa0e9427aad2d1902. --- .../cross/ros1_dependencies_build_scripts/1037-mailutils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils index d95efeaefe..510b004c88 100755 --- a/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils +++ b/jsk_unitree_robot/cross/ros1_dependencies_build_scripts/1037-mailutils @@ -14,6 +14,6 @@ done cd ${SOURCE_DIR} -./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies --disable-python +./configure --prefix=/opt/jsk/${INSTALL_ROOT}/ros1_dependencies make install From f13a43d536696a23700734c71bc0401df9573366 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Tue, 26 Jul 2022 10:58:38 +0900 Subject: [PATCH 54/77] enable to send mail --- .../scripts/smach_to_mail.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py new file mode 100644 index 0000000000..4a038b4281 --- /dev/null +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +import base64 +import cv2 +import datetime +import imghdr +import pickle +import rospy +import time + +from cv_bridge import CvBridge +from jsk_robot_startup.msg import Email +from jsk_robot_startup.msg import EmailBody +from sensor_msgs.msg import CompressedImage +from smach_msgs.msg import SmachContainerStatus + + +class SmachToMail(): + + def __init__(self): + rospy.init_node('server_name') + # it should be 'smach_to_mail', but 'server_name' + # is the default name of smach_ros + self.pub = rospy.Publisher("/email", Email, queue_size=10) + rospy.Subscriber( + "~smach/container_status", SmachContainerStatus, self._status_cb) + self.bridge = CvBridge() + self.smach_state_list = [] # for status list + self.sender_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" + self.receiver_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" + + def _status_cb(self, msg): + ''' + Recording starts when smach_state becomes start. + ''' + if len(msg.active_states) == 0: + return + file_path = None + status_str = ', '.join(msg.active_states); + local_data_str = pickle.loads(msg.local_data) + info_str = msg.info + print("status -> {}".format(status_str)) + print("description_str -> {}".format(local_data_str['DESCRIPTION'])) + + if status_str == "START" or "INIT": + self.smach_state_list = [] + if local_data_str['IMAGE']: + imgmsg = CompressedImage() + imgmsg.deserialize(base64.b64decode(local_data_str['IMAGE'])) + cv_image = self.bridge.compressed_imgmsg_to_cv2(imgmsg, "bgr8") + rospy.logerr( + "cv image type:{}".format(imghdr.what(None, cv_image))) # for debugging + dt_now = datetime.datetime.now() # header should be used?? + + file_path = "/tmp/{}_{}.jpg".format( + status_str.lower(), dt_now.strftime('%y%m%d%H%M%S')) + rospy.loginfo("filepath:{}".format(file_path)) + # if (next((x for x in self.smach_state_list if x["IMAGE"] == file_path), None)): + # rospy.loginfo("same file name!!!!") + # else: + # rospy.loginfo("not same file name!!!") + cv2.imwrite(file_path, cv_image) + # cv2.imshow("Image", input_image) + cv2.waitKey(2) + else: + file_path = "" + + status_dict = {'DESCRIPTION': local_data_str['DESCRIPTION'], + 'IMAGE': file_path} # dict is complicated? + self.smach_state_list.append(status_dict) + print(self.smach_state_list) + print("info_str -> {}".format(info_str)) + + if status_str == "END" or "FINISH": + rospy.loginfo("END!!") + self._send_mail() + + def _send_mail(self): + email_msg = Email() + email_msg.body = [] + changeline = EmailBody() + changeline.type = 'html' + changeline.message = "
    " + for x in self.smach_state_list: + description = EmailBody() + image = EmailBody() + description.type = 'text' + description.message = x['DESCRIPTION'] + email_msg.body.append(description) + email_msg.body.append(changeline) + if x['IMAGE']: + image.type = 'img' + image.img_size = 50 + image.file_path = x['IMAGE'] + email_msg.body.append(image) + email_msg.body.append(changeline) + rospy.loginfo("body:{}".format(email_msg.body)) + + email_msg.subject = 'Smach mail test' + + email_msg.sender_address = self.sender_address + email_msg.receiver_address = self.receiver_address + + time.sleep(1) + self.pub.publish(email_msg) + + +if __name__ == '__main__': + stm = SmachToMail() + rospy.spin() From 2fe8f2190a883fbc96304619155e5316528db1fd Mon Sep 17 00:00:00 2001 From: Kei Okada Date: Wed, 27 Jul 2022 12:49:11 +0900 Subject: [PATCH 55/77] add more typecheck, use rospy.loginfo for message --- .../scripts/smach_to_mail.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) mode change 100644 => 100755 jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py old mode 100644 new mode 100755 index 4a038b4281..86434748bf --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -33,18 +33,27 @@ def _status_cb(self, msg): ''' Recording starts when smach_state becomes start. ''' + rospy.loginfo("Received SMACH status") if len(msg.active_states) == 0: return file_path = None status_str = ', '.join(msg.active_states); local_data_str = pickle.loads(msg.local_data) info_str = msg.info - print("status -> {}".format(status_str)) - print("description_str -> {}".format(local_data_str['DESCRIPTION'])) + if not type(local_data_str) is dict: + rospy.logerr("local_data_str:({}) is not dictionary".format(local_data_str)) + rospy.logerr("May be you forget to pass user data in exec-state-machine ?") + rospy.logerr("please check your program execute smach with (exec-state-machine (sm) '((description . "")(image . "")))") + + rospy.loginfo("- status -> {}".format(status_str)) + if local_data_str.has_key('DESCRIPTION'): + rospy.loginfo("- description_str -> {}".format(local_data_str['DESCRIPTION'])) + else: + rospy.logwarn("smach does not have DESCRIPTION, see https://github.com/jsk-ros-pkg/jsk_robot/tree/master/jsk_robot_common/jsk_robot_startup#smach_to_mailpy for more info") if status_str == "START" or "INIT": self.smach_state_list = [] - if local_data_str['IMAGE']: + if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: imgmsg = CompressedImage() imgmsg.deserialize(base64.b64decode(local_data_str['IMAGE'])) cv_image = self.bridge.compressed_imgmsg_to_cv2(imgmsg, "bgr8") @@ -54,7 +63,7 @@ def _status_cb(self, msg): file_path = "/tmp/{}_{}.jpg".format( status_str.lower(), dt_now.strftime('%y%m%d%H%M%S')) - rospy.loginfo("filepath:{}".format(file_path)) + rospy.loginfo("- filepath:{}".format(file_path)) # if (next((x for x in self.smach_state_list if x["IMAGE"] == file_path), None)): # rospy.loginfo("same file name!!!!") # else: @@ -65,13 +74,16 @@ def _status_cb(self, msg): else: file_path = "" - status_dict = {'DESCRIPTION': local_data_str['DESCRIPTION'], - 'IMAGE': file_path} # dict is complicated? + status_dict = {} + if local_data_str.has_key('DESCRIPTION'): + status_dict.update({'DESCRIPTION': local_data_str['DESCRIPTION']}) + if file_path != "": + status_dict.update({'IMAGE': file_path}) # dict is complicated? + self.smach_state_list.append(status_dict) - print(self.smach_state_list) - print("info_str -> {}".format(info_str)) + rospy.loginfo("- info_str -> {}".format(info_str)) - if status_str == "END" or "FINISH": + if status_str in ["END", "FINISH"]: rospy.loginfo("END!!") self._send_mail() @@ -85,10 +97,11 @@ def _send_mail(self): description = EmailBody() image = EmailBody() description.type = 'text' - description.message = x['DESCRIPTION'] + if x.has_key('DESCRIPTION'): + description.message = x['DESCRIPTION'] email_msg.body.append(description) email_msg.body.append(changeline) - if x['IMAGE']: + if x.has_key('IMAGE') and x['IMAGE']: image.type = 'img' image.img_size = 50 image.file_path = x['IMAGE'] From d4f0597171ce3ceee97dd30adf404545a35fc85a Mon Sep 17 00:00:00 2001 From: Kei Okada Date: Wed, 27 Jul 2022 22:19:31 +0900 Subject: [PATCH 56/77] use https://github.com/tkmtnt7000/jsk_robot/pull/5 to publish image string, not file, set image resolution to 640, use DESCRIPTION of START node as mail subject, warn if we received status without START node --- .../scripts/smach_to_mail.py | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index 86434748bf..22795b95b1 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -25,7 +25,7 @@ def __init__(self): rospy.Subscriber( "~smach/container_status", SmachContainerStatus, self._status_cb) self.bridge = CvBridge() - self.smach_state_list = [] # for status list + self.smach_state_list = None # for status list self.sender_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" self.receiver_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" @@ -46,46 +46,51 @@ def _status_cb(self, msg): rospy.logerr("please check your program execute smach with (exec-state-machine (sm) '((description . "")(image . "")))") rospy.loginfo("- status -> {}".format(status_str)) + rospy.loginfo("- info_str -> {}".format(info_str)) if local_data_str.has_key('DESCRIPTION'): rospy.loginfo("- description_str -> {}".format(local_data_str['DESCRIPTION'])) else: rospy.logwarn("smach does not have DESCRIPTION, see https://github.com/jsk-ros-pkg/jsk_robot/tree/master/jsk_robot_common/jsk_robot_startup#smach_to_mailpy for more info") + if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: + rospy.loginfo("- image_str -> {}".format(local_data_str['IMAGE'][:64])) - if status_str == "START" or "INIT": + if status_str in ["START", "INIT"]: self.smach_state_list = [] - if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: - imgmsg = CompressedImage() - imgmsg.deserialize(base64.b64decode(local_data_str['IMAGE'])) - cv_image = self.bridge.compressed_imgmsg_to_cv2(imgmsg, "bgr8") - rospy.logerr( - "cv image type:{}".format(imghdr.what(None, cv_image))) # for debugging - dt_now = datetime.datetime.now() # header should be used?? - - file_path = "/tmp/{}_{}.jpg".format( - status_str.lower(), dt_now.strftime('%y%m%d%H%M%S')) - rospy.loginfo("- filepath:{}".format(file_path)) - # if (next((x for x in self.smach_state_list if x["IMAGE"] == file_path), None)): - # rospy.loginfo("same file name!!!!") - # else: - # rospy.loginfo("not same file name!!!") - cv2.imwrite(file_path, cv_image) - # cv2.imshow("Image", input_image) - cv2.waitKey(2) - else: - file_path = "" + # DESCRIPTION of START is MAIL SUBJECT + if local_data_str.has_key('DESCRIPTION'): + self.smach_state_subject = local_data_str['DESCRIPTION'] + del local_data_str['DESCRIPTION'] + else: + self.smach_state_subject = None status_dict = {} if local_data_str.has_key('DESCRIPTION'): status_dict.update({'DESCRIPTION': local_data_str['DESCRIPTION']}) - if file_path != "": - status_dict.update({'IMAGE': file_path}) # dict is complicated? - self.smach_state_list.append(status_dict) - rospy.loginfo("- info_str -> {}".format(info_str)) + if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: + imgmsg = CompressedImage() + imgmsg.deserialize(base64.b64decode(local_data_str['IMAGE'])) + cv_image = self.bridge.compressed_imgmsg_to_cv2(imgmsg, "bgr8") + scale_percent = 640.0 / cv_image.shape[1] * 100.0 + width = int(cv_image.shape[1] * scale_percent / 100) + height = int(cv_image.shape[0] * scale_percent / 100) + dim = (width, height) + cv_image = cv2.resize(cv_image, dim, interpolation = cv2.INTER_AREA) + status_dict.update({'IMAGE': base64.b64encode(cv2.imencode('.jpg', cv_image)[1].tostring())}) # dict is complicated? + + if self.smach_state_list == None: + rospy.logwarn("received {}, but we did not find START node".format(status_dict)) + else: + self.smach_state_list.append(status_dict) if status_str in ["END", "FINISH"]: - rospy.loginfo("END!!") - self._send_mail() + if self.smach_state_list == None: + rospy.logwarn("received END node, but we did not find START node") + rospy.logwarn("failed to send {}".format(status_dict)) + else: + rospy.loginfo("END!!") + self._send_mail() + self.smach_state_list = None def _send_mail(self): email_msg = Email() @@ -94,27 +99,31 @@ def _send_mail(self): changeline.type = 'html' changeline.message = "
    " for x in self.smach_state_list: - description = EmailBody() - image = EmailBody() - description.type = 'text' if x.has_key('DESCRIPTION'): + description = EmailBody() + description.type = 'text' description.message = x['DESCRIPTION'] - email_msg.body.append(description) - email_msg.body.append(changeline) + email_msg.body.append(description) + email_msg.body.append(changeline) if x.has_key('IMAGE') and x['IMAGE']: + image = EmailBody() image.type = 'img' - image.img_size = 50 - image.file_path = x['IMAGE'] + image.img_size = 100 + image.img_data = x['IMAGE'] email_msg.body.append(image) email_msg.body.append(changeline) - rospy.loginfo("body:{}".format(email_msg.body)) + # rospy.loginfo("body:{}".format(email_msg.body)) - email_msg.subject = 'Smach mail test' + if self.smach_state_subject: + email_msg.subject = self.smach_state_subject + else: + email_msg.subject = 'Message from {}'.format(rospy.get_param('/robot/name', 'robot')) email_msg.sender_address = self.sender_address email_msg.receiver_address = self.receiver_address - time.sleep(1) + rospy.loginfo("send '{}' email to {}".format(email_msg.subject, email_msg.receiver_address)) + self.pub.publish(email_msg) From cc1777f3b371c2ea80b5fbe86b9310b434c3cc18 Mon Sep 17 00:00:00 2001 From: Kei Okada Date: Tue, 9 Aug 2022 11:51:33 +0900 Subject: [PATCH 57/77] enable to receive multiple smach state --- .../scripts/smach_to_mail.py | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index 22795b95b1..9ef2338b59 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -25,7 +25,8 @@ def __init__(self): rospy.Subscriber( "~smach/container_status", SmachContainerStatus, self._status_cb) self.bridge = CvBridge() - self.smach_state_list = None # for status list + self.smach_state_list = {} # for status list + self.smach_state_subject = {} # for status subject self.sender_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" self.receiver_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" @@ -36,7 +37,8 @@ def _status_cb(self, msg): rospy.loginfo("Received SMACH status") if len(msg.active_states) == 0: return - file_path = None + + # Print received messages status_str = ', '.join(msg.active_states); local_data_str = pickle.loads(msg.local_data) info_str = msg.info @@ -54,15 +56,21 @@ def _status_cb(self, msg): if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: rospy.loginfo("- image_str -> {}".format(local_data_str['IMAGE'][:64])) + # Store data for every callerid to self.smach_state_list[caller_id] + caller_id = msg._connection_header['callerid'] + + # If we received START/INIT status, restart storing smach_state_list if status_str in ["START", "INIT"]: - self.smach_state_list = [] + self.smach_state_list[caller_id] = [] # DESCRIPTION of START is MAIL SUBJECT if local_data_str.has_key('DESCRIPTION'): - self.smach_state_subject = local_data_str['DESCRIPTION'] + self.smach_state_subject[caller_id] = local_data_str['DESCRIPTION'] del local_data_str['DESCRIPTION'] else: - self.smach_state_subject = None + self.smach_state_subject[celler_id] = None + # Build status_dict for every status + # expected keys are 'DESCRIPTION' , 'IMAGE', 'STATE', 'INFO', 'TIME' status_dict = {} if local_data_str.has_key('DESCRIPTION'): status_dict.update({'DESCRIPTION': local_data_str['DESCRIPTION']}) @@ -77,28 +85,36 @@ def _status_cb(self, msg): dim = (width, height) cv_image = cv2.resize(cv_image, dim, interpolation = cv2.INTER_AREA) status_dict.update({'IMAGE': base64.b64encode(cv2.imencode('.jpg', cv_image)[1].tostring())}) # dict is complicated? + status_dict.update({'STATE': status_str}) + status_dict.update({'INFO': info_str}) + status_dict.update({'TIME': datetime.datetime.fromtimestamp(msg.header.stamp.to_sec())}) - if self.smach_state_list == None: + if ( not self.smach_state_list.has_key(caller_id) ) or self.smach_state_list[caller_id] == None: rospy.logwarn("received {}, but we did not find START node".format(status_dict)) else: - self.smach_state_list.append(status_dict) + self.smach_state_list[caller_id].append(status_dict) + # If we received END/FINISH status, send email, etc... if status_str in ["END", "FINISH"]: - if self.smach_state_list == None: + if ( not self.smach_state_list.has_key(caller_id) ) or self.smach_state_list[caller_id] == None: rospy.logwarn("received END node, but we did not find START node") rospy.logwarn("failed to send {}".format(status_dict)) else: rospy.loginfo("END!!") - self._send_mail() - self.smach_state_list = None - - def _send_mail(self): + rospy.loginfo("Following SMACH is reported") + for x in self.smach_state_list[caller_id]: + rospy.loginfo(" - At {}, Active state is {}{}".format(x['TIME'], x['STATE'], + "({})".format(x['INFO']) if x['INFO'] else '')) + self._send_mail(self.smach_state_subject[caller_id], self.smach_state_list[caller_id]) + self.smach_state_list[caller_id] = None + + def _send_mail(self, subject, state_list): email_msg = Email() email_msg.body = [] changeline = EmailBody() changeline.type = 'html' changeline.message = "
    " - for x in self.smach_state_list: + for x in state_list: if x.has_key('DESCRIPTION'): description = EmailBody() description.type = 'text' @@ -114,8 +130,8 @@ def _send_mail(self): email_msg.body.append(changeline) # rospy.loginfo("body:{}".format(email_msg.body)) - if self.smach_state_subject: - email_msg.subject = self.smach_state_subject + if subject: + email_msg.subject = subject else: email_msg.subject = 'Message from {}'.format(rospy.get_param('/robot/name', 'robot')) From 0dd909e84e7ab731e9b36d4629600b1a8cf78577 Mon Sep 17 00:00:00 2001 From: Kei Okada Date: Tue, 9 Aug 2022 16:21:18 +0900 Subject: [PATCH 58/77] add to pubilsh /tweet message --- .../scripts/smach_to_mail.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index 9ef2338b59..bde3899494 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -13,6 +13,7 @@ from jsk_robot_startup.msg import EmailBody from sensor_msgs.msg import CompressedImage from smach_msgs.msg import SmachContainerStatus +from std_msgs.msg import String class SmachToMail(): @@ -21,7 +22,8 @@ def __init__(self): rospy.init_node('server_name') # it should be 'smach_to_mail', but 'server_name' # is the default name of smach_ros - self.pub = rospy.Publisher("/email", Email, queue_size=10) + self.pub_email = rospy.Publisher("email", Email, queue_size=10) + self.pub_twitter = rospy.Publisher("tweet", String, queue_size=10) rospy.Subscriber( "~smach/container_status", SmachContainerStatus, self._status_cb) self.bridge = CvBridge() @@ -106,6 +108,7 @@ def _status_cb(self, msg): rospy.loginfo(" - At {}, Active state is {}{}".format(x['TIME'], x['STATE'], "({})".format(x['INFO']) if x['INFO'] else '')) self._send_mail(self.smach_state_subject[caller_id], self.smach_state_list[caller_id]) + self._send_twitter(self.smach_state_subject[caller_id], self.smach_state_list[caller_id]) self.smach_state_list[caller_id] = None def _send_mail(self, subject, state_list): @@ -140,7 +143,22 @@ def _send_mail(self, subject, state_list): rospy.loginfo("send '{}' email to {}".format(email_msg.subject, email_msg.receiver_address)) - self.pub.publish(email_msg) + self.pub_email.publish(email_msg) + + def _send_twitter(self, subject, state_list): + if subject: + self.pub_twitter.publish(String(subject)) + rospy.loginfo("send '{}' to twitter".format(subject)) + + for x in state_list: + text = "" + if x.has_key('DESCRIPTION'): + text = x['DESCRIPTION'] + if x.has_key('IMAGE') and x['IMAGE']: + text += x['IMAGE'] + if len(text) > 1: + self.pub_twitter.publish(String(text)) + rospy.loginfo("send '{}' to twitter".format(text[0:144])) if __name__ == '__main__': From 0bfbf7901fe9025d7f2755850b22da4b4766f4f2 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Thu, 28 Jul 2022 13:09:15 +0900 Subject: [PATCH 59/77] [jsk_robot_startup] Fix dependent on linter --- .../scripts/smach_to_mail.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index bde3899494..b81d5a4ad4 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -41,8 +41,12 @@ def _status_cb(self, msg): return # Print received messages - status_str = ', '.join(msg.active_states); - local_data_str = pickle.loads(msg.local_data) + status_str = ', '.join(msg.active_states) + if sys.version_info.major < 3: + local_data_str = pickle.loads(msg.local_data) + else: + local_data_str = pickle.loads( + msg.local_data.encode('utf-8'), encoding='utf-8') info_str = msg.info if not type(local_data_str) is dict: rospy.logerr("local_data_str:({}) is not dictionary".format(local_data_str)) @@ -51,11 +55,11 @@ def _status_cb(self, msg): rospy.loginfo("- status -> {}".format(status_str)) rospy.loginfo("- info_str -> {}".format(info_str)) - if local_data_str.has_key('DESCRIPTION'): + if 'DESCRIPTION' in local_data_str: rospy.loginfo("- description_str -> {}".format(local_data_str['DESCRIPTION'])) else: rospy.logwarn("smach does not have DESCRIPTION, see https://github.com/jsk-ros-pkg/jsk_robot/tree/master/jsk_robot_common/jsk_robot_startup#smach_to_mailpy for more info") - if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: + if 'IMAGE' in local_data_str and local_data_str['IMAGE']: rospy.loginfo("- image_str -> {}".format(local_data_str['IMAGE'][:64])) # Store data for every callerid to self.smach_state_list[caller_id] @@ -65,7 +69,7 @@ def _status_cb(self, msg): if status_str in ["START", "INIT"]: self.smach_state_list[caller_id] = [] # DESCRIPTION of START is MAIL SUBJECT - if local_data_str.has_key('DESCRIPTION'): + if 'DESCRIPTION' in local_data_str: self.smach_state_subject[caller_id] = local_data_str['DESCRIPTION'] del local_data_str['DESCRIPTION'] else: @@ -74,10 +78,10 @@ def _status_cb(self, msg): # Build status_dict for every status # expected keys are 'DESCRIPTION' , 'IMAGE', 'STATE', 'INFO', 'TIME' status_dict = {} - if local_data_str.has_key('DESCRIPTION'): + if 'DESCRIPTION' in local_data_str: status_dict.update({'DESCRIPTION': local_data_str['DESCRIPTION']}) - if local_data_str.has_key('IMAGE') and local_data_str['IMAGE']: + if 'IMAGE' in local_data_str and local_data_str['IMAGE']: imgmsg = CompressedImage() imgmsg.deserialize(base64.b64decode(local_data_str['IMAGE'])) cv_image = self.bridge.compressed_imgmsg_to_cv2(imgmsg, "bgr8") @@ -91,14 +95,15 @@ def _status_cb(self, msg): status_dict.update({'INFO': info_str}) status_dict.update({'TIME': datetime.datetime.fromtimestamp(msg.header.stamp.to_sec())}) - if ( not self.smach_state_list.has_key(caller_id) ) or self.smach_state_list[caller_id] == None: + + if (caller_id not in self.smach_state_list) or self.smach_state_list[caller_id] is None: rospy.logwarn("received {}, but we did not find START node".format(status_dict)) else: self.smach_state_list[caller_id].append(status_dict) # If we received END/FINISH status, send email, etc... if status_str in ["END", "FINISH"]: - if ( not self.smach_state_list.has_key(caller_id) ) or self.smach_state_list[caller_id] == None: + if (caller_id not in self.smach_state_list) or self.smach_state_list[caller_id] is None: rospy.logwarn("received END node, but we did not find START node") rospy.logwarn("failed to send {}".format(status_dict)) else: @@ -118,13 +123,13 @@ def _send_mail(self, subject, state_list): changeline.type = 'html' changeline.message = "
    " for x in state_list: - if x.has_key('DESCRIPTION'): + if 'DESCRIPTION' in x: description = EmailBody() description.type = 'text' description.message = x['DESCRIPTION'] email_msg.body.append(description) email_msg.body.append(changeline) - if x.has_key('IMAGE') and x['IMAGE']: + if 'IMAGE' in x and x['IMAGE']: image = EmailBody() image.type = 'img' image.img_size = 100 @@ -152,9 +157,9 @@ def _send_twitter(self, subject, state_list): for x in state_list: text = "" - if x.has_key('DESCRIPTION'): + if 'DESCRIPTION' in x: text = x['DESCRIPTION'] - if x.has_key('IMAGE') and x['IMAGE']: + if 'IMAGE' in x and x['IMAGE']: text += x['IMAGE'] if len(text) > 1: self.pub_twitter.publish(String(text)) From 7d1958ed30c44b2341e0d1b5842b6b0aa0ad9c84 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Thu, 28 Jul 2022 18:53:52 +0900 Subject: [PATCH 60/77] [jsk_robot_startup] Support python3 in smach_to_mail.py --- jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index b81d5a4ad4..b28a7be6c5 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -7,6 +7,7 @@ import pickle import rospy import time +import sys from cv_bridge import CvBridge from jsk_robot_startup.msg import Email From a935e96b922a0d62f23b6b4d31ce20a3f39ee44a Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Tue, 9 Aug 2022 23:24:14 +0900 Subject: [PATCH 61/77] [jsk_robot_startup/smach_mail] Fix typo --- jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index b28a7be6c5..6bbd550dbf 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -74,7 +74,7 @@ def _status_cb(self, msg): self.smach_state_subject[caller_id] = local_data_str['DESCRIPTION'] del local_data_str['DESCRIPTION'] else: - self.smach_state_subject[celler_id] = None + self.smach_state_subject[caller_id] = None # Build status_dict for every status # expected keys are 'DESCRIPTION' , 'IMAGE', 'STATE', 'INFO', 'TIME' From f3933dd9650f691cab63927058e19b897e940bf4 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Tue, 9 Aug 2022 23:25:10 +0900 Subject: [PATCH 62/77] [jsk_robot_startup/smach_mail] Remove unused module --- jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index 6bbd550dbf..8033afef74 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -3,10 +3,8 @@ import base64 import cv2 import datetime -import imghdr import pickle import rospy -import time import sys from cv_bridge import CvBridge From 3d41642dae1045afc5fc0d5dfd0e87bf79d212db Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Tue, 9 Aug 2022 23:30:05 +0900 Subject: [PATCH 63/77] [jsk_robot_startup/smach_to_mail] Set sender address and receiver address from rosparam --- jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index 8033afef74..15642bb4f8 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -28,8 +28,8 @@ def __init__(self): self.bridge = CvBridge() self.smach_state_list = {} # for status list self.smach_state_subject = {} # for status subject - self.sender_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" - self.receiver_address = "tsukamoto@jsk.imi.i.u-tokyo.ac.jp" + self.sender_address = rospy.get_param("~sender_address") + self.receiver_address = rospy.get_param("~receiver_address") def _status_cb(self, msg): ''' From 2542efdbbaa3e5ac1d3ffd229a84187616ae19fc Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Mon, 22 Aug 2022 10:31:25 +0900 Subject: [PATCH 64/77] [jsk_robot_startup/smach_to_mail] Raise error when no address is set --- .../jsk_robot_startup/scripts/smach_to_mail.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index 15642bb4f8..fff6597cd2 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -28,8 +28,17 @@ def __init__(self): self.bridge = CvBridge() self.smach_state_list = {} # for status list self.smach_state_subject = {} # for status subject - self.sender_address = rospy.get_param("~sender_address") - self.receiver_address = rospy.get_param("~receiver_address") + if rospy.has_param("~sender_address"): + self.sender_address = rospy.get_param("~sender_address") + else: + rospy.logerr("Please set rosparam {}/sender_address".format( + rospy.get_name())) + if rospy.has_param("~receiver_address"): + self.receiver_address = rospy.get_param("~receiver_address") + else: + rospy.logerr("Please set rosparam {}/receiver_address".format( + rospy.get_name())) + def _status_cb(self, msg): ''' From 10757570bca8be56f6585227844f4877723f0677 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Mon, 22 Aug 2022 15:37:02 +0900 Subject: [PATCH 65/77] [jsk_robot_startup/smach_to_mail] Use yaml file to set address --- .../scripts/smach_to_mail.py | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index fff6597cd2..c49d73cc32 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -3,9 +3,11 @@ import base64 import cv2 import datetime +import os import pickle import rospy import sys +import yaml from cv_bridge import CvBridge from jsk_robot_startup.msg import Email @@ -28,16 +30,27 @@ def __init__(self): self.bridge = CvBridge() self.smach_state_list = {} # for status list self.smach_state_subject = {} # for status subject - if rospy.has_param("~sender_address"): - self.sender_address = rospy.get_param("~sender_address") + yaml_path = rospy.get_param( + '~email_info', "/var/lib/robot/email_info.yaml") + if os.path.exists(yaml_path): + with open(yaml_path) as yaml_f: + self.email_info = yaml.load(yaml_f) + rospy.loginfo( + "{} is loaded as email config file.".format(yaml_path)) + rospy.loginfo(self.email_info) + self.sender_address = self.email_info['sender_address'] + self.receiver_address = self.email_info['receiver_address'] else: - rospy.logerr("Please set rosparam {}/sender_address".format( - rospy.get_name())) - if rospy.has_param("~receiver_address"): - self.receiver_address = rospy.get_param("~receiver_address") - else: - rospy.logerr("Please set rosparam {}/receiver_address".format( + if rospy.has_param("~sender_address"): + self.sender_address = rospy.get_param("~sender_address") + else: + rospy.logerr("Please set rosparam {}/sender_address".format( rospy.get_name())) + if rospy.has_param("~receiver_address"): + self.receiver_address = rospy.get_param("~receiver_address") + else: + rospy.logerr("Please set rosparam {}/receiver_address".format( + rospy.get_name())) def _status_cb(self, msg): From 54a3d1a7a1a404524aa1d4fbc3183cb00fbd812b Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 22 Aug 2022 17:37:50 +0900 Subject: [PATCH 66/77] [jsk_robot_startup] Publish whole /tweet at a time --- .../scripts/smach_to_mail.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py index c49d73cc32..33b7f8eac6 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/smach_to_mail.py @@ -172,19 +172,19 @@ def _send_mail(self, subject, state_list): self.pub_email.publish(email_msg) def _send_twitter(self, subject, state_list): + text = "" if subject: - self.pub_twitter.publish(String(subject)) - rospy.loginfo("send '{}' to twitter".format(subject)) - + text += subject for x in state_list: - text = "" - if 'DESCRIPTION' in x: - text = x['DESCRIPTION'] + if 'DESCRIPTION' in x and x['DESCRIPTION']: + text += '\n' + x['DESCRIPTION'] if 'IMAGE' in x and x['IMAGE']: - text += x['IMAGE'] - if len(text) > 1: - self.pub_twitter.publish(String(text)) - rospy.loginfo("send '{}' to twitter".format(text[0:144])) + img_txt = x['IMAGE'] + if isinstance(img_txt, bytes): + img_txt = img_txt.decode('ascii') + text += img_txt + if len(text) > 1: + self.pub_twitter.publish(String(text)) if __name__ == '__main__': From 7a4f0186f197b940c609c0574fdd071d3ca186a5 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Tue, 23 Aug 2022 14:42:08 +0900 Subject: [PATCH 67/77] [jsk_robot_startup] Add Readme for smach_to_mail.py --- jsk_robot_common/jsk_robot_startup/README.md | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/jsk_robot_common/jsk_robot_startup/README.md b/jsk_robot_common/jsk_robot_startup/README.md index ce43e215a6..5544d7016e 100644 --- a/jsk_robot_common/jsk_robot_startup/README.md +++ b/jsk_robot_common/jsk_robot_startup/README.md @@ -209,6 +209,59 @@ rostopic pub /reboot std_msgs/Empty ``` +## scripts/smach_to_mail.py + +This node sends smach messages to `/email`, `/tweet`, etc... to notify robot state transition. + +```mermaid +flowchart TB + subgraph S[Demo Code Running on SMACH] + START --> State_1 + State_1 --> State_2 + State_2 --> END + end + subgraph E[SMACH To Notification] + id1[(state list
    DESCRIPTION / IMAGE
    DESCRIPTION / IMAGE
    ...)] + id1 --> email[[pub /email jsk_robot_startup::Email]] + id1 --> tweet[[pub /tweet std_msgs::String]] + id1 --> chat[[pub /google_chat_ros/send/goal
    google_chat_ros::SendMessageAction]] + end + S -->|"[{'DESCRIPTION': string, 'IMAGE': base64}]"| E + email --> email_body(["DESCRIPTION[0]
    DESCRIPTION[1]
    IMAGE[1]
    DESCRIPTION[1]
    IMAGE[1]
    ..."]) + tweet --> tweet_body1(["DESCRIPTION[0]"]) + tweet_body1 --> tweet_body2(["DESCRIPTION[1]
    IMAGE[1]"]) + tweet_body2 --> tweet_body3(["DESCRIPTION[2]
    IMAGE[2]"]) + chat --> chat_body1(["DESCRIPTION[0]"]) + chat_body1 --> chat_body2(["DESCRIPTION[1]
    IMAGE[1]"]) + chat_body2 --> chat_body3(["DESCRIPTION[2]
    IMAGE[2]"]) +``` + +### Subscribing Topics + +* `~smach/container_status` (`smach_msgs/SmachContainerStatus`) + + Input topic smach status + +### Publishing Topics + +* `/email` (`jsk_robot_startup/Email`) + + Email message with description and image + +* `/tweet` (`std_msgs/String`) + + Tweet message with description and image + +### Parameters + +* `~sender_address` (String) + + Sender address + +* `~receiver_address` (String) + + Receiver address + ## launch/safe_teleop.launch This launch file provides a set of nodes for safe teleoperation common to mobile robots. Robot-specific nodes such as `/joy`, `/teleop` or `/cable_warning` must be included in the teleop launch file for each robot, such as [safe_teleop.xml for PR2](https://github.com/jsk-ros-pkg/jsk_robot/blob/master/jsk_pr2_robot/jsk_pr2_startup/jsk_pr2_move_base/safe_teleop.xml) or [safe_teleop.xml for fetch](https://github.com/jsk-ros-pkg/jsk_robot/blob/master/jsk_fetch_robot/jsk_fetch_startup/launch/fetch_teleop.xml). From cfdfc6e52a781f6acc896e7e5ce985566ce3b26d Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 29 Aug 2022 15:21:28 +0900 Subject: [PATCH 68/77] Revert "[jsk_unitree_startup] Launch trt pose on jetson nano2gb (192.168.123.13)" This reverts commit 8d9eb4a462e34bd62732f70f49e5c2cf1d22f8bd. --- jsk_unitree_robot/cross/install.sh | 14 +++----------- .../jsk_unitree_startup/autostart/imageai.sh | 3 +-- .../scripts/publish_human_pose.diff | 17 +---------------- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index 767bb10b03..fce1d02c5f 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -137,7 +137,6 @@ function copy_data () { if [[ ${TYPE} == "Pro" ]] ; then copy_data pi 192.168.123.161 - copy_data unitree 192.168.123.13 copy_data unitree 192.168.123.14 ## copy_data unitree 192.168.123.15 : Pro : No Space for auto start elif [[ ${TYPE} == "Air" ]] ; then @@ -148,10 +147,8 @@ else fi if [[ "${TARGET_DIRECTORY}" == "User" ]]; then - cuda_ip="192.168.123.13" if [[ ${TYPE} == "Pro" ]] ; then - sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/src/jsk_robot/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh unitree@192.168.123.13:/home/unitree/Unitree/autostart/imageai/imageai.sh - sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/src/jsk_robot/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh unitree@192.168.123.15:/home/unitree/Unitree/autostart/imageai/imageai.sh + cuda_ip="192.168.123.15" elif [[ ${TYPE} == "Air" ]] ; then cuda_ip="192.168.123.13" fi @@ -163,13 +160,8 @@ if [[ "${TARGET_DIRECTORY}" == "User" ]]; then sshpass -p 123 scp ${TARGET_MACHINE}_${TARGET_DIRECTORY}/src/jsk_robot/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff unitree@${cuda_ip}:/tmp/publish_human_pose.diff sshpass -p 123 ssh -t unitree@${cuda_ip} bash -c 'ls; OUT="$(patch -p0 --backup --forward /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/pyScripts/live_human_pose.py < /tmp/publish_human_pose.diff | tee /dev/tty)" || echo "${OUT}" | grep "Skipping patch" -q || (echo "$OUT" && false);' - # launch live_human_pose.py on jetson nano (192.168.123.13) - sshpass -p 123 ssh -t unitree@${cuda_ip} "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/pyScripts/live_human_pose.py" - # replace 192.168.123.15 -> 192.168.123.13 because live_human_pose.py is running on jetson nano (192.168.123.13) - if [[ ${TYPE} == "Pro" ]] ; then - sshpass -p 123 ssh -t unitree@192.168.123.13 "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/config/mqSNNRConfig.yaml" - sshpass -p 123 ssh -t unitree@192.168.123.14 "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/config/mqSNNLConfig.yaml" - sshpass -p 123 ssh -t unitree@192.168.123.15 "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/config/mqMNConfig.yaml" + if [[ ${TYPE} == "Air" ]] ; then + sshpass -p 123 ssh -t unitree@${cuda_ip} "sed -i 's/192.168.123.15/192.168.123.13/g' /home/unitree/Unitree/autostart/imageai/mLComSystemFrame/pyScripts/live_human_pose.py" fi fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh index 6f98fd581b..6401998441 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh +++ b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh @@ -52,7 +52,7 @@ if [ $localIPAddr == $Nano3 ];then echo " MasterNano" ############### MasterNano doing things!!!! gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqMNConfig.yaml; exec bash" - # gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" + gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" elif [ $localIPAddr == $Nano2 ];then echo " SecNanoLeft" @@ -65,7 +65,6 @@ elif [ $localIPAddr == $Nano1 ];then ############### SecNanoRight(1-2) doing things!!!! gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml; exec bash" gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml 1; exec bash" - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" else echo " Not Found $localIPAddr in IP-Clump!" fi diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff b/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff index cfae1f90d7..960c221f2f 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff @@ -1,12 +1,6 @@ --- live_human_pose.py 2022-05-26 00:17:04.634784206 +0900 +++ live_human_pose_jsk.py 2022-05-26 00:17:18.102931810 +0900 -@@ -1,4 +1,5 @@ - #coding:utf-8 -+import base64 - import json - import trt_pose . coco - import trt_pose . models -@@ -169,6 +171,8 @@ +@@ -169,6 +169,8 @@ if 39 - 39: o00ooo0 - II111iiii * OoO0O00 % o0oOOo0O0Ooo * II111iiii % II111iiii cv2 . circle ( src , ( i1I1iI , o0O ) , 8 , I1i1I1II , - 1 ) iI1Ii11111iIi . write ( src ) @@ -15,12 +9,3 @@ # cv2 . imshow ( "ai" , src ) # cv2 . waitKey ( 1 ) if 59 - 59: iIii1I11I1II1 + I1IiiI - o0oOOo0O0Ooo - I1IiiI + Oo / I1ii11iIi11i -@@ -199,6 +203,8 @@ - print ( "AI is working ...." , camIndex . value ) - Oo0oOOo , Oo0OoO00oOO0o = o0OOO [ camIndex . value - 1 ] . read ( ) - OOO00O = time . time ( ) -+ # add by iory 2022.8.6 -+ O0ii1ii1ii.publish("vision/front_camera", base64.b64encode(cv2.imencode('.jpg', Oo0OoO00oOO0o, [int(cv2.IMWRITE_JPEG_QUALITY), 90])[1]).decode('ascii')) - OOoOO0oo0ooO = cv2 . resize ( Oo0OoO00oOO0o , dsize = ( OooO0 , II11iiii1Ii ) , interpolation = cv2 . INTER_AREA ) - iI ( OOoOO0oo0ooO , Oo0OoO00oOO0o , OOO00O ) - if 98 - 98: I1II1 * I1II1 / I1II1 + O00ooOO From eb02f63649d661bc212adbf59261b4d631868ce3 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 29 Aug 2022 15:21:32 +0900 Subject: [PATCH 69/77] Revert "[jsk_unitree_startup] Add imageai.sh which is running on default unitree pro" This reverts commit f8e3778dbe0ae069009adc75ef77571720b73953. --- .../jsk_unitree_startup/autostart/imageai.sh | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100755 jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh diff --git a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh b/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh deleted file mode 100755 index 6401998441..0000000000 --- a/jsk_unitree_robot/jsk_unitree_startup/autostart/imageai.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -Nano1="192.168.123.13" ## SecNanoRight(1-2) -Nano2="192.168.123.14" ## SecNanoLeft(3-4) -Nano3="192.168.123.15" ## MasterNano(5) -eval echo "[imageai] starting ... " $toStartlog - -mLRootPATH="/home/unitree/Unitree/autostart/imageai/" -updatePackagePATH="/home/unitree/mLComSystemFrame.tar.gz" - -echo -e "\e[1;32m**** 1. Check if the program needs to be updated ? ****\e[0m" -checkIFUpdate(){ - if [ -f "$updatePackagePATH" ];then - echo -e "\e[1;32m The update file exists, it will be updating soon ...\e[0m" - tar -zxvf $updatePackagePATH -C $mLRootPATH > /dev/null; sleep 1 - rm -rf $updatePackagePATH - echo -e "\e[1;32m Updating Success.\e[0m" - else - echo -e "\e[1;31m NO Need to update!\e[0m" - fi -} - -checkIFUpdate -# dd=$? - -#declare -i lossNano1=-1 -#declare -i lossNano2=-1 -#declare -i lossNano3=-1 - -#echo -e "\e[1;32m**** 2. Check if all Nano'IP Address is OK ? ****\e[0m" - -#until (( lossNano1 + lossNano2 + lossNano3 == 0 )) -#do -# lossNano1=`ping -c 2 -w 2 $Nano1 | grep loss | awk '{print $6}' | awk -F "%" '{print $1}'` -# echo $lossNano1 -# -# lossNano2=`ping -c 2 -w 2 $Nano2 | grep loss | awk '{print $6}' | awk -F "%" '{print $1}'` -# echo $lossNano2 -# -# lossNano3=`ping -c 2 -w 2 $Nano3 | grep loss | awk '{print $6}' | awk -F "%" '{print $1}'` -# echo $lossNano3 -# #sleep 10 -#done - -#echo " IP is Right!" - -## GET PC's Address -localIPAddr=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6| head -n 1 | awk '{print $2}'|tr -d "addr:"` -echo "Local Address: "$localIPAddr - -echo -e "\e[1;32m**** 2. Start Main Application ... ****\e[0m" -if [ $localIPAddr == $Nano3 ];then - echo " MasterNano" - ############### MasterNano doing things!!!! - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqMNConfig.yaml; exec bash" - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/pyScripts; python3 live_human_pose.py; exec bash" - -elif [ $localIPAddr == $Nano2 ];then - echo " SecNanoLeft" - ############### SecNanoLeft(3-4) doing things!!!! - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNLConfig.yaml; exec bash" - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNLConfig.yaml 1; exec bash" - -elif [ $localIPAddr == $Nano1 ];then - echo " SecNanoRight" - ############### SecNanoRight(1-2) doing things!!!! - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml; exec bash" - gnome-terminal -- bash -c "export GST_PLUGIN_PATH=/home/unitree/Unitree/autostart/imageai/mLComSystemFrame/ThirdParty/webSinkPipe/build; cd $mLRootPATH/mLComSystemFrame/bin; ./mqttControlNode ../config/mqSNNRConfig.yaml 1; exec bash" -else - echo " Not Found $localIPAddr in IP-Clump!" -fi - -echo " EveryThing is done!" From 20cd79eac29787d443f8965858da6af50fa74212 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 29 Aug 2022 15:24:29 +0900 Subject: [PATCH 70/77] [jsk_unitree_startup] Copy data to jetson nano 2gb (192.168.123.13) --- jsk_unitree_robot/cross/install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/jsk_unitree_robot/cross/install.sh b/jsk_unitree_robot/cross/install.sh index fce1d02c5f..85029d4d77 100755 --- a/jsk_unitree_robot/cross/install.sh +++ b/jsk_unitree_robot/cross/install.sh @@ -137,6 +137,7 @@ function copy_data () { if [[ ${TYPE} == "Pro" ]] ; then copy_data pi 192.168.123.161 + copy_data unitree 192.168.123.13 copy_data unitree 192.168.123.14 ## copy_data unitree 192.168.123.15 : Pro : No Space for auto start elif [[ ${TYPE} == "Air" ]] ; then From 70cc9f94099aa26a6a2b0f0fa8920b4dc447f2b9 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 29 Aug 2022 15:24:48 +0900 Subject: [PATCH 71/77] [jsk_unitree_startup] Publish image via mqtt --- .../scripts/publish_human_pose.diff | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff b/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff index 960c221f2f..cfae1f90d7 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/publish_human_pose.diff @@ -1,6 +1,12 @@ --- live_human_pose.py 2022-05-26 00:17:04.634784206 +0900 +++ live_human_pose_jsk.py 2022-05-26 00:17:18.102931810 +0900 -@@ -169,6 +169,8 @@ +@@ -1,4 +1,5 @@ + #coding:utf-8 ++import base64 + import json + import trt_pose . coco + import trt_pose . models +@@ -169,6 +171,8 @@ if 39 - 39: o00ooo0 - II111iiii * OoO0O00 % o0oOOo0O0Ooo * II111iiii % II111iiii cv2 . circle ( src , ( i1I1iI , o0O ) , 8 , I1i1I1II , - 1 ) iI1Ii11111iIi . write ( src ) @@ -9,3 +15,12 @@ # cv2 . imshow ( "ai" , src ) # cv2 . waitKey ( 1 ) if 59 - 59: iIii1I11I1II1 + I1IiiI - o0oOOo0O0Ooo - I1IiiI + Oo / I1ii11iIi11i +@@ -199,6 +203,8 @@ + print ( "AI is working ...." , camIndex . value ) + Oo0oOOo , Oo0OoO00oOO0o = o0OOO [ camIndex . value - 1 ] . read ( ) + OOO00O = time . time ( ) ++ # add by iory 2022.8.6 ++ O0ii1ii1ii.publish("vision/front_camera", base64.b64encode(cv2.imencode('.jpg', Oo0OoO00oOO0o, [int(cv2.IMWRITE_JPEG_QUALITY), 90])[1]).decode('ascii')) + OOoOO0oo0ooO = cv2 . resize ( Oo0OoO00oOO0o , dsize = ( OooO0 , II11iiii1Ii ) , interpolation = cv2 . INTER_AREA ) + iI ( OOoOO0oo0ooO , Oo0OoO00oOO0o , OOO00O ) + if 98 - 98: I1II1 * I1II1 / I1II1 + O00ooOO From 096165e0660c94434fa4cfa2a57208fd5d8db9f0 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Tue, 24 May 2022 22:11:49 +0900 Subject: [PATCH 72/77] [jsk_robot_startup] Add EmailBody msg --- jsk_robot_common/jsk_robot_startup/CMakeLists.txt | 1 + jsk_robot_common/jsk_robot_startup/msg/Email.msg | 2 +- jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg diff --git a/jsk_robot_common/jsk_robot_startup/CMakeLists.txt b/jsk_robot_common/jsk_robot_startup/CMakeLists.txt index 7f597875af..a9ca7f482e 100644 --- a/jsk_robot_common/jsk_robot_startup/CMakeLists.txt +++ b/jsk_robot_common/jsk_robot_startup/CMakeLists.txt @@ -59,6 +59,7 @@ add_message_files( FILES RoomLight.msg Email.msg + EmailBody.msg ) generate_messages( diff --git a/jsk_robot_common/jsk_robot_startup/msg/Email.msg b/jsk_robot_common/jsk_robot_startup/msg/Email.msg index 5726aa1477..08cc108e30 100644 --- a/jsk_robot_common/jsk_robot_startup/msg/Email.msg +++ b/jsk_robot_common/jsk_robot_startup/msg/Email.msg @@ -1,6 +1,6 @@ std_msgs/Header header string subject # Email subject -string body # Email body +jsk_robot_startup/EmailBody[] body # Email body string sender_address # Sender's email address string receiver_address # receiver's email address string smtp_server # smtp server address diff --git a/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg b/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg new file mode 100644 index 0000000000..0d50971cef --- /dev/null +++ b/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg @@ -0,0 +1,3 @@ +string type # text, html, img +string message # For text type +string file_path # For img type From 8763099df66eb57bf7c0ba716875aabf77c06030 Mon Sep 17 00:00:00 2001 From: Naoto Tsukamoto Date: Wed, 25 May 2022 14:47:44 +0900 Subject: [PATCH 73/77] [jsk_robot_startup] Support changing embed image size and change msg explanation --- .../jsk_robot_startup/msg/EmailBody.msg | 3 +- .../jsk_robot_startup/scripts/email_topic.py | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg b/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg index 0d50971cef..e93849b3d7 100644 --- a/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg +++ b/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg @@ -1,3 +1,4 @@ string type # text, html, img -string message # For text type +string message # For text and html type string file_path # For img type +uint8 img_size # For img type [percent] diff --git a/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py b/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py index 9918e174d8..10b309a75c 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py @@ -76,6 +76,36 @@ def _send_mail( msg["From"] = sender_address msg["To"] = receiver_address msg.attach(MIMEText(body, 'plain', 'utf-8')) + # Support embed image + for content in body: + if content.type == 'text': + msg.attach(MIMEText(content.message, 'plain', 'utf-8')) + elif content.type == 'html': + msg.attach(MIMEText(content.message, 'html')) + elif content.type == 'img': + if content.file_path == '': + rospy.logwarn('File name is empty. Skipped.') + continue + if not os.path.exists(content.file_path): + rospy.logerr( + 'File {} is not found.'.format(content.file_path)) + return + with open(content.file_path, 'rb') as img: + embed_img = MIMEImage(img.read()) + embed_img.add_header( + 'Content-ID', '<{}>'.format(content.file_path)) + msg.attach(embed_img) # This line is necessary to embed + if content.img_size: + image_size = content.img_size + else: + image_size = 100 + text = ''.format( + content.file_path, image_size) + bodytext = MIMEText(text, 'html') + msg.attach(bodytext) + else: + rospy.logwarn('Unknown content type {}'.format(content.type)) + continue # Attach file for attached_file in attached_files: if attached_file == '': From 56f41d74f9ca5b0c0fe8012c665a2565055dc065 Mon Sep 17 00:00:00 2001 From: Kei Okada Date: Wed, 27 Jul 2022 18:44:43 +0900 Subject: [PATCH 74/77] add img_data to EmailBody, update email_topic.py and email-topic-client.l --- .../euslisp/email-topic-client.l | 20 +++++++++- .../euslisp/sample-email-topic-client.l | 32 +++++++++++++-- .../jsk_robot_startup/msg/EmailBody.msg | 3 +- .../jsk_robot_startup/scripts/email_topic.py | 40 ++++++++++++------- 4 files changed, 75 insertions(+), 20 deletions(-) diff --git a/jsk_robot_common/jsk_robot_startup/euslisp/email-topic-client.l b/jsk_robot_common/jsk_robot_startup/euslisp/email-topic-client.l index a78d46de09..8def28a2ff 100644 --- a/jsk_robot_common/jsk_robot_startup/euslisp/email-topic-client.l +++ b/jsk_robot_common/jsk_robot_startup/euslisp/email-topic-client.l @@ -1,5 +1,7 @@ (ros::load-ros-manifest "jsk_robot_startup") +(require :base64 "lib/llib/base64.l") + (defun init-mail () (ros::advertise "email" jsk_robot_startup::Email 1) (ros::spin-once) @@ -16,7 +18,23 @@ (setq msg (instance jsk_robot_startup::Email :init)) (send msg :header :stamp (ros::time-now)) (send msg :subject subject) - (send msg :body body) + (send msg :body + (mapcar #'(lambda (e) + (cond ((derivedp e image-2d) + (if (> (length (send e :entity)) (* 8 8192)) + (warning-message 3 "The size of img is too large (~A)~%You may encounter 'too long string' error, see https://github.com/euslisp/EusLisp/issues/2 for more info~%" (length (send e :entity)))) + (instance jsk_robot_startup::EmailBody :init :type "img" + :img_data (base64encode (send e :entity)) + :img_size 100)) + ((probe-file e) + (instance jsk_robot_startup::EmailBody :init :type "img" :file_path e :img_size 100)) + ((and (> (length e) 0) ;; check html + (eq (elt e 0) #\<) + (eq (elt e (1- (length e))) #\>)) + (instance jsk_robot_startup::EmailBody :init :type "html" :message e)) + (t + (instance jsk_robot_startup::EmailBody :init :type "text" :message (format nil "~A" e))))) + (if (atom body) (list body) body))) (send msg :sender_address sender-address) (send msg :receiver_address receiver-address) (send msg :smtp_server smtp-server) diff --git a/jsk_robot_common/jsk_robot_startup/euslisp/sample-email-topic-client.l b/jsk_robot_common/jsk_robot_startup/euslisp/sample-email-topic-client.l index 855b5f9fef..ad280eca65 100755 --- a/jsk_robot_common/jsk_robot_startup/euslisp/sample-email-topic-client.l +++ b/jsk_robot_common/jsk_robot_startup/euslisp/sample-email-topic-client.l @@ -7,10 +7,36 @@ (ros::spin-once) (setq receiver-address (ros::get-param "~receiver_address")) (setq attached-files (ros::get-param "~attached_files" nil)) -(unix::sleep 10) ;; wait for server +(while (or (null (ros::get-num-subscribers "email")) + (<= (ros::get-num-subscribers "email") 0)) + (ros::ros-warn "wait for email topic") + (unix::sleep 1)) (ros::ros-info "Sending a mail to ~A" receiver-address) -(send-mail "test-mail" receiver-address "test" :attached-files attached-files) -(ros::ros-info "Sent a mail") +(send-mail "test text mail" receiver-address "test") +(ros::ros-info "Sent a text mail") +(unix:sleep 1) + +(send-mail "test html mail" receiver-address "

    test with html mail

    ") +(ros::ros-info "Sent a html mail") +(unix:sleep 1) + +(send-mail "test attached mail" receiver-address "test with attached image" :attached-files attached-files) +(ros::ros-info "Sent a mail with attached files ~A" attached-files) +(unix:sleep 1) + +(when attached-files + (send-mail "test image embedded mail (file)" receiver-address (append attached-files (list "test with embedded image"))) + (ros::ros-info "sent a mail with embedded image ~a" attached-files) + (unix:sleep 1) + ;; read png/jpeg file, but do not extract to raw image, keep original compressed data in (img . entity) + (setq img (read-image-file (elt attached-files 0))) + (setq (img . entity) (make-string (elt (unix:stat (elt attached-files 0)) 7))) ;; size of file + (with-open-file (f (elt attached-files 0)) (unix:uread (send f :infd) (img . entity))) + (send-mail "test image embedded mail (string)" receiver-address (list "test with embedded image" img)) + (ros::ros-info "Sent a mail with embedded image ~A" img) + (unix:sleep 1) + ) + (ros::roseus "shutdown") (exit) diff --git a/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg b/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg index e93849b3d7..db3626ff0a 100644 --- a/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg +++ b/jsk_robot_common/jsk_robot_startup/msg/EmailBody.msg @@ -1,4 +1,5 @@ string type # text, html, img string message # For text and html type -string file_path # For img type +string file_path # For img type, set file path +string img_data # For img type, you can also use base64 encoded image data instead of file path uint8 img_size # For img type [percent] diff --git a/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py b/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py index 10b309a75c..4ec7f06f5b 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/email_topic.py @@ -1,6 +1,7 @@ #!/usr/bin/env python from email.mime.application import MIMEApplication +from email.mime.image import MIMEImage from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import errno @@ -12,6 +13,9 @@ import socket from socket import error as socket_error import yaml +import copy +import random, string # for image cid +import base64 class EmailTopic(object): @@ -43,7 +47,11 @@ def __init__(self): 'email', Email, self._cb, queue_size=1) def _cb(self, msg): - rospy.loginfo('Received an msg: {}'.format(msg)) + msg_compact = copy.deepcopy(msg) + for content in msg_compact.body: + if len(content.img_data) >= 64: + content.img_data = content.img_data[:64] + "...." + rospy.loginfo('Received an msg: {}'.format(msg_compact)) send_mail_args = {} # Set default value for self._send_mail arguments send_mail_args['subject'] = '' @@ -75,7 +83,6 @@ def _send_mail( msg["Subject"] = subject msg["From"] = sender_address msg["To"] = receiver_address - msg.attach(MIMEText(body, 'plain', 'utf-8')) # Support embed image for content in body: if content.type == 'text': @@ -83,24 +90,27 @@ def _send_mail( elif content.type == 'html': msg.attach(MIMEText(content.message, 'html')) elif content.type == 'img': - if content.file_path == '': - rospy.logwarn('File name is empty. Skipped.') + if content.img_data != '': + embed_img = MIMEImage(base64.b64decode(content.img_data)) + cid = ''.join(random.choice(string.ascii_lowercase) for i in range(16)) + elif os.path.exists(content.file_path): + with open(content.file_path, 'rb') as img: + embed_img = MIMEImage(img.read()) + cid = content.file_path + else: + rospy.logerr("'img' content requries either file_path {}".format(content.type)) + rospy.logerr(" or img_data {} with img_format {}".format(content.img_data, content.img_format)) continue - if not os.path.exists(content.file_path): - rospy.logerr( - 'File {} is not found.'.format(content.file_path)) - return - with open(content.file_path, 'rb') as img: - embed_img = MIMEImage(img.read()) - embed_img.add_header( - 'Content-ID', '<{}>'.format(content.file_path)) - msg.attach(embed_img) # This line is necessary to embed + embed_img.add_header( + 'Content-ID', '<{}>'.format(cid)) + embed_img.add_header( + 'Content-Disposition', 'inline; filename="{}"'.format(os.path.basename(cid))) + msg.attach(embed_img) # This line is necessary to embed if content.img_size: image_size = content.img_size else: image_size = 100 - text = ''.format( - content.file_path, image_size) + text = ''.format(cid, image_size) bodytext = MIMEText(text, 'html') msg.attach(bodytext) else: From 61209bc89990d9be04dd2b6f00b3b85e760f42c5 Mon Sep 17 00:00:00 2001 From: Kei Okada Date: Wed, 29 Jun 2022 20:29:33 +0900 Subject: [PATCH 75/77] add node to publish smach_state of lead_teleop program --- .../launch/lead_teleop.launch | 4 ++ .../scripts/lead-teleop-state.l | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100755 jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l diff --git a/jsk_unitree_robot/jsk_unitree_startup/launch/lead_teleop.launch b/jsk_unitree_robot/jsk_unitree_startup/launch/lead_teleop.launch index 975f4c99df..c4339a9d80 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/launch/lead_teleop.launch +++ b/jsk_unitree_robot/jsk_unitree_startup/launch/lead_teleop.launch @@ -29,4 +29,8 @@ + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l new file mode 100755 index 0000000000..4a6095f05f --- /dev/null +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l @@ -0,0 +1,61 @@ +#!/usr/bin/env roseus + +(load "package://roseus_smach/src/state-machine-ros.l") + +(ros::roseus-add-msgs "jsk_recognition_msgs") + +(setq find-person-msg (instance jsk_recognition_msgs::PeoplePoseArray :init)) +(defun find-person-cb (msg) + (setq find-person-msg msg)) + +(defun start-func (args) + (let () + (set-alist 'description "Start Walking" args) + :started)) + +(defun walk-func (args) + (let ((last-time-seen-person 9999)) + (while (> last-time-seen-person 5) + (setq last-time-seen-person (send (ros::time- (ros::time-now) (send find-person-msg :header :stamp)) :to-sec)) + (ros::ros-info "walk : detect person ~A sec ago" last-time-seen-person) + (ros::spin-once) + (ros::sleep)) + ;; set description and goes to next state + (set-alist 'description "Find Person" args) + :find-person)) + +(defun find-person-func (args) + (let ((last-time-seen-person 0)) + (while (< last-time-seen-person 5) + (setq last-time-seen-person (send (ros::time- (ros::time-now) (send find-person-msg :header :stamp)) :to-sec)) + (ros::ros-info "person: detect person ~A sec ago" last-time-seen-person) + (ros::spin-once) + (ros::sleep)) + ; set description and goes to next state + (set-alist 'description "Start Walking" args) + :walk)) + +(defun lead-teleop-sm () + (let (sm) + (setq sm + (make-state-machine + '((:start :started :walk) + (:walk :find-person :find-person) + (:walk :finished :end) + (:find-person :walk :walk)) + '((:start 'start-func) + (:end '(lambda (&rest args) :finished)) + (:walk 'walk-func) ;; function maps + (:find-person 'find-person-func)) + '(:start) ;; initial + '(:end) ;; goal + )) + (send sm :arg-keys 'description) + sm)) + +(ros::roseus "lead-teleop-smach") +(ros::rate 1) +(ros::subscribe "/people_pose" jsk_recognition_msgs::PeoplePoseArray #'find-person-cb) +;; state machine +(exec-state-machine (lead-teleop-sm)) +(ros::exit) From c3b18f9ef34cc310de2fdd5a36b75f97429a4984 Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 29 Aug 2022 16:55:40 +0900 Subject: [PATCH 76/77] [jsk_unitree_startup/human_pose_publisher.py] Add timestamp for people pose --- .../jsk_unitree_startup/scripts/human_pose_publisher.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/human_pose_publisher.py b/jsk_unitree_robot/jsk_unitree_startup/scripts/human_pose_publisher.py index c0a130588a..930f0a907f 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/human_pose_publisher.py +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/human_pose_publisher.py @@ -84,6 +84,7 @@ def on_message(client, userdata, msg): rospy.loginfo("Received `{}` from `{}` topic".format(msg.payload.decode(), msg.topic)) poses_msg = PeoplePoseArray() + poses_msg.header.stamp = self.last_received for topic in eval(msg.payload.decode()): # convert str to list by eval() pose_msg = PeoplePose() for limb, pos in topic.items(): From e6ab4830fa8f6ffc3d17b97906ccbd140e4a629c Mon Sep 17 00:00:00 2001 From: iory Date: Mon, 29 Aug 2022 16:56:00 +0900 Subject: [PATCH 77/77] [jsk_unitree_startup/llead-teleop-state.l] Check detected person --- .../jsk_unitree_startup/scripts/lead-teleop-state.l | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l b/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l index 4a6095f05f..63ee24a480 100755 --- a/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l +++ b/jsk_unitree_robot/jsk_unitree_startup/scripts/lead-teleop-state.l @@ -16,7 +16,8 @@ (defun walk-func (args) (let ((last-time-seen-person 9999)) (while (> last-time-seen-person 5) - (setq last-time-seen-person (send (ros::time- (ros::time-now) (send find-person-msg :header :stamp)) :to-sec)) + (when (> (length (send find-person-msg :poses)) 0) + (setq last-time-seen-person (send (ros::time- (ros::time-now) (send find-person-msg :header :stamp)) :to-sec))) (ros::ros-info "walk : detect person ~A sec ago" last-time-seen-person) (ros::spin-once) (ros::sleep)) @@ -27,7 +28,8 @@ (defun find-person-func (args) (let ((last-time-seen-person 0)) (while (< last-time-seen-person 5) - (setq last-time-seen-person (send (ros::time- (ros::time-now) (send find-person-msg :header :stamp)) :to-sec)) + (when (> (length (send find-person-msg :poses)) 0) + (setq last-time-seen-person (send (ros::time- (ros::time-now) (send find-person-msg :header :stamp)) :to-sec))) (ros::ros-info "person: detect person ~A sec ago" last-time-seen-person) (ros::spin-once) (ros::sleep))