diff --git a/.travis.rosinstall.melodic b/.travis.rosinstall.melodic index d9add05bce..278b28a7f6 100644 --- a/.travis.rosinstall.melodic +++ b/.travis.rosinstall.melodic @@ -54,3 +54,15 @@ local-name: DENSORobot/denso_cobotta_ros uri: https://github.com/DENSORobot/denso_cobotta_ros.git version: bb60e75adb8477ed3402561b4ec3ba687af3f397 +# switchbot_ros is not correctly released. +# see: https://github.com/jsk-ros-pkg/jsk_3rdparty/issues/356 +- git: + local-name: jsk-ros-pkg/jsk_3rdparty + uri: https://github.com/jsk-ros-pkg/jsk_3rdparty.git + version: f0ab7bba54523b8f9945ed4a3e9c0efec0c8dde9 +# for jsk_spot_teleop in jsk_robot +# spot_msgs is not released +- git: + local-name: clearpathrobotics/spot_ros + uri: https://github.com/clearpathrobotics/spot_ros.git + version: 8e1554b95cf2875a53f14fda8165957559b0ffb7 \ No newline at end of file diff --git a/jsk_aero_robot/aeroeus/CMakeLists.txt b/jsk_aero_robot/aeroeus/CMakeLists.txt index 38590b4cd4..8f3d7c7127 100644 --- a/jsk_aero_robot/aeroeus/CMakeLists.txt +++ b/jsk_aero_robot/aeroeus/CMakeLists.txt @@ -8,4 +8,5 @@ catkin_package( ## rosrun euscollada collada2eus_urdfmodel $(rospack find aero_description)/robots/aero.urdf $(rospack find aero_description)/robots/aero.yaml aero.l -install(FILES aero-interface.l aero-hand.l aero-wheels.l DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) diff --git a/jsk_aero_robot/jsk_aero_startup/CMakeLists.txt b/jsk_aero_robot/jsk_aero_startup/CMakeLists.txt index 94a9bdf257..9e060e3e0e 100644 --- a/jsk_aero_robot/jsk_aero_startup/CMakeLists.txt +++ b/jsk_aero_robot/jsk_aero_startup/CMakeLists.txt @@ -10,7 +10,7 @@ endif() find_package(catkin REQUIRED) catkin_package() -install(DIRECTORY launch raw_maps scripts +install(DIRECTORY launch raw_maps scripts config test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) diff --git a/jsk_baxter_robot/baxtereus/CMakeLists.txt b/jsk_baxter_robot/baxtereus/CMakeLists.txt index d75a0e0bc8..5a4e1fe79a 100644 --- a/jsk_baxter_robot/baxtereus/CMakeLists.txt +++ b/jsk_baxter_robot/baxtereus/CMakeLists.txt @@ -65,7 +65,8 @@ install(DIRECTORY euslisp test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) -install(FILES baxter.l baxter-interface.l baxter-util.l baxter.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} baxter.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) if(CATKIN_ENABLE_TESTING) find_package(catkin REQUIRED COMPONENTS rostest) diff --git a/jsk_baxter_robot/jsk_baxter_startup/CMakeLists.txt b/jsk_baxter_robot/jsk_baxter_startup/CMakeLists.txt index 51ff998466..f4eb3b6789 100644 --- a/jsk_baxter_robot/jsk_baxter_startup/CMakeLists.txt +++ b/jsk_baxter_robot/jsk_baxter_startup/CMakeLists.txt @@ -17,9 +17,10 @@ include_directories( catkin_add_env_hooks(99.jsk_baxter_startup SHELLS bash zsh DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/env-hooks) -install(DIRECTORY config jsk_baxter_joy jsk_baxter_lifelog jsk_baxter_moveit jsk_baxter_sensors jsk_baxter_tools +install(DIRECTORY config jsk_baxter_joy jsk_baxter_lifelog jsk_baxter_moveit jsk_baxter_sensors jsk_baxter_tools jsk_baxter_description jsk_baxter_machine jsk_baxter_udev DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) -install(FILES baxter.launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +file(GLOB LAUNCH_FILES *.launch) +install(FILES ${LAUNCH_FILES} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) diff --git a/jsk_baxter_robot/jsk_baxter_web/CMakeLists.txt b/jsk_baxter_robot/jsk_baxter_web/CMakeLists.txt index 9c7072a085..a61eefcd87 100644 --- a/jsk_baxter_robot/jsk_baxter_web/CMakeLists.txt +++ b/jsk_baxter_robot/jsk_baxter_web/CMakeLists.txt @@ -29,6 +29,6 @@ include_directories( ${catkin_INCLUDE_DIRS} ) -install(DIRECTORY launch +install(DIRECTORY launch www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) diff --git a/jsk_denso_robot/cobottaeus/CMakeLists.txt b/jsk_denso_robot/cobottaeus/CMakeLists.txt index e59ae6d705..ac455f9fb2 100644 --- a/jsk_denso_robot/cobottaeus/CMakeLists.txt +++ b/jsk_denso_robot/cobottaeus/CMakeLists.txt @@ -43,11 +43,12 @@ add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/cobotta.l add_custom_target(compile_cobotta ALL DEPENDS ${PROJECT_SOURCE_DIR}/cobotta.l) -install(DIRECTORY euslisp test +install(DIRECTORY sample test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) -install(FILES cobotta.l cobotta-interface.l cobotta-util.l cobotta.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} cobotta.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) if(CATKIN_ENABLE_TESTING) find_package(catkin REQUIRED COMPONENTS rostest) diff --git a/jsk_denso_robot/jsk_cobotta_startup/CMakeLists.txt b/jsk_denso_robot/jsk_cobotta_startup/CMakeLists.txt index 50ceb95ec7..805e919076 100644 --- a/jsk_denso_robot/jsk_cobotta_startup/CMakeLists.txt +++ b/jsk_denso_robot/jsk_cobotta_startup/CMakeLists.txt @@ -33,12 +33,12 @@ catkin_package() ############# ## Install ## ############# -install(DIRECTORY config launch scripts data +install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) -install(FILES jsk_cobotta.machine - DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +# install(FILES jsk_cobotta.machine +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) ############# ## Testing ## diff --git a/jsk_fetch_robot/jsk_fetch_gazebo_demo/CMakeLists.txt b/jsk_fetch_robot/jsk_fetch_gazebo_demo/CMakeLists.txt index f8ab04eead..319eb6314f 100644 --- a/jsk_fetch_robot/jsk_fetch_gazebo_demo/CMakeLists.txt +++ b/jsk_fetch_robot/jsk_fetch_gazebo_demo/CMakeLists.txt @@ -4,7 +4,7 @@ project(jsk_fetch_gazebo_demo) find_package(catkin) catkin_package() -install(DIRECTORY euslisp launch config +install(DIRECTORY euslisp launch config test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS ) diff --git a/jsk_fetch_robot/jsk_fetch_startup/CMakeLists.txt b/jsk_fetch_robot/jsk_fetch_startup/CMakeLists.txt index be9259dac6..2e813fb0f8 100644 --- a/jsk_fetch_robot/jsk_fetch_startup/CMakeLists.txt +++ b/jsk_fetch_robot/jsk_fetch_startup/CMakeLists.txt @@ -23,7 +23,15 @@ catkin_add_env_hooks(99.jsk_fetch_startup SHELLS bash zsh sh ############# ## Install ## ############# -install(DIRECTORY config launch scripts data +file(GLOB_RECURSE SCRIPT_PROGRAMS scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts/) + endif() +endforeach() +install(DIRECTORY config launch data apps euslisp robots supervisor_scripts tmuxinator_yaml udev_rules upstart_scripts DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) diff --git a/jsk_kinova_robot/jsk_kinova_startup/CMakeLists.txt b/jsk_kinova_robot/jsk_kinova_startup/CMakeLists.txt index c0e3d84fbd..c50bda2127 100644 --- a/jsk_kinova_robot/jsk_kinova_startup/CMakeLists.txt +++ b/jsk_kinova_robot/jsk_kinova_startup/CMakeLists.txt @@ -14,7 +14,7 @@ catkin_package() ############# ## Install ## ############# -install(DIRECTORY config launch +install(DIRECTORY config launch scripts DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) diff --git a/jsk_kinova_robot/kinovaeus/CMakeLists.txt b/jsk_kinova_robot/kinovaeus/CMakeLists.txt index b6f51a120b..2ef0ba696f 100644 --- a/jsk_kinova_robot/kinovaeus/CMakeLists.txt +++ b/jsk_kinova_robot/kinovaeus/CMakeLists.txt @@ -67,7 +67,7 @@ install(DIRECTORY test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) -install(FILES kinova.l kinova-interface.l kinova-util.l DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +install(FILES kinova.l kinova-interface.l kinova-util.l gen3_robotiq_2f_85.yaml gen3_robotiq_2f_140.yaml gen3_lite_gen3_lite_2f.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) if(CATKIN_ENABLE_TESTING) find_package(catkin REQUIRED COMPONENTS rostest) diff --git a/jsk_magni_robot/jsk_magni_startup/CMakeLists.txt b/jsk_magni_robot/jsk_magni_startup/CMakeLists.txt index 33876c3948..cbf50ad329 100644 --- a/jsk_magni_robot/jsk_magni_startup/CMakeLists.txt +++ b/jsk_magni_robot/jsk_magni_startup/CMakeLists.txt @@ -14,7 +14,7 @@ catkin_package() ############# ## Install ## ############# -install(DIRECTORY launch +install(DIRECTORY launch data DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) diff --git a/jsk_naoqi_robot/jsk_nao_startup/CMakeLists.txt b/jsk_naoqi_robot/jsk_nao_startup/CMakeLists.txt index 4a945f7df0..a708f3dbb8 100644 --- a/jsk_naoqi_robot/jsk_nao_startup/CMakeLists.txt +++ b/jsk_naoqi_robot/jsk_nao_startup/CMakeLists.txt @@ -13,7 +13,7 @@ find_package(catkin REQUIRED COMPONENTS) ## Uncomment this if the package has a setup.py. This macro ensures ## modules and global scripts declared therein get installed ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() +catkin_python_setup() ################################################ ## Declare ROS messages, services and actions ## @@ -130,6 +130,22 @@ include_directories( # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} # ) +file(GLOB_RECURSE SCRIPT_PROGRAMS nodes/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/nodes) + endif() +endforeach() +file(GLOB_RECURSE SCRIPT_PROGRAMS scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts) + endif() +endforeach() ## Mark cpp header files for installation # install(DIRECTORY include/${PROJECT_NAME}/ @@ -137,6 +153,9 @@ include_directories( # FILES_MATCHING PATTERN "*.h" # PATTERN ".svn" EXCLUDE # ) +install(DIRECTORY launch + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS) ## Mark other files for installation (e.g. launch and bag files, etc.) # install(FILES diff --git a/jsk_naoqi_robot/jsk_nao_startup/setup.py b/jsk_naoqi_robot/jsk_nao_startup/setup.py new file mode 100644 index 0000000000..5a423aaf02 --- /dev/null +++ b/jsk_naoqi_robot/jsk_nao_startup/setup.py @@ -0,0 +1,9 @@ +## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD + +from distutils.core import setup +from catkin_pkg.python_setup import generate_distutils_setup + +# fetch values from package.xml +setup_args = generate_distutils_setup() + +setup(**setup_args) diff --git a/jsk_naoqi_robot/jsk_pepper_startup/CMakeLists.txt b/jsk_naoqi_robot/jsk_pepper_startup/CMakeLists.txt index 4b9e09ae9f..e25a9339f1 100644 --- a/jsk_naoqi_robot/jsk_pepper_startup/CMakeLists.txt +++ b/jsk_naoqi_robot/jsk_pepper_startup/CMakeLists.txt @@ -3,11 +3,21 @@ project(jsk_pepper_startup) find_package(catkin REQUIRED COMPONENTS) +catkin_python_setup() + catkin_package() -install(DIRECTORY launch nodes sample +install(DIRECTORY launch nodes sample apps config DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) +file(GLOB_RECURSE SCRIPT_PROGRAMS script/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/script) + endif() +endforeach() if(CATKIN_ENABLE_TESTING AND NOT $ENV{ROS_DISTRO} STREQUAL "hydro") find_package(catkin REQUIRED COMPONENTS rostest roslaunch) diff --git a/jsk_naoqi_robot/jsk_pepper_startup/setup.py b/jsk_naoqi_robot/jsk_pepper_startup/setup.py new file mode 100644 index 0000000000..5a423aaf02 --- /dev/null +++ b/jsk_naoqi_robot/jsk_pepper_startup/setup.py @@ -0,0 +1,9 @@ +## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD + +from distutils.core import setup +from catkin_pkg.python_setup import generate_distutils_setup + +# fetch values from package.xml +setup_args = generate_distutils_setup() + +setup(**setup_args) diff --git a/jsk_naoqi_robot/naoeus/CMakeLists.txt b/jsk_naoqi_robot/naoeus/CMakeLists.txt index 9457324d22..5a1e382a0c 100644 --- a/jsk_naoqi_robot/naoeus/CMakeLists.txt +++ b/jsk_naoqi_robot/naoeus/CMakeLists.txt @@ -19,3 +19,13 @@ compile_naoqi_model(nao naoV50_generated_urdf) if(nao_meshes_FOUND) add_rostest(test/naoeus.test) endif() + +### +### install +### +install(DIRECTORY patch test launch sample webots euslisp + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS) +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} nao.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + diff --git a/jsk_naoqi_robot/peppereus/CMakeLists.txt b/jsk_naoqi_robot/peppereus/CMakeLists.txt index bf179714e7..1889e522e5 100644 --- a/jsk_naoqi_robot/peppereus/CMakeLists.txt +++ b/jsk_naoqi_robot/peppereus/CMakeLists.txt @@ -20,4 +20,11 @@ if(pepper_meshes_FOUND AND CATKIN_ENABLE_TESTING) add_rostest(test/peppereus.test) endif() - +### +### install +### +install(DIRECTORY doc test + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS) +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} pepper.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) diff --git a/jsk_pr2_robot/jsk_pr2_calibration/CMakeLists.txt b/jsk_pr2_robot/jsk_pr2_calibration/CMakeLists.txt index 73fdf60e27..bc76135f60 100644 --- a/jsk_pr2_robot/jsk_pr2_calibration/CMakeLists.txt +++ b/jsk_pr2_robot/jsk_pr2_calibration/CMakeLists.txt @@ -4,3 +4,7 @@ project(jsk_pr2_calibration) find_package(catkin REQUIRED COMPONENTS) catkin_package() + +install(DIRECTORY scripts + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS) diff --git a/jsk_pr2_robot/jsk_pr2_startup/CMakeLists.txt b/jsk_pr2_robot/jsk_pr2_startup/CMakeLists.txt index b1981968a9..77c3d71a12 100644 --- a/jsk_pr2_robot/jsk_pr2_startup/CMakeLists.txt +++ b/jsk_pr2_robot/jsk_pr2_startup/CMakeLists.txt @@ -9,7 +9,9 @@ catkin_add_env_hooks(99.jsk_pr2_startup SHELLS bash zsh install(DIRECTORY config jsk_pr2_image_transport jsk_pr2_joy jsk_pr2_lifelog jsk_pr2_move_base jsk_pr2_moveit + jsk_pr2_perception jsk_pr2_rocon jsk_pr2_teleop jsk_pr2_sensors jsk_pr2_warning src sample + apps DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) file(GLOB _LAUNCH_FILES ${PROJECT_SOURCE_DIR}/ *.launch) # add / to avoid to get current directory @@ -18,5 +20,5 @@ foreach(_LAUNCH_FILE ${_LAUNCH_FILES}) DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) endforeach() -install(FILES install_pr1040_description.sh jsk_pr2.machine plugin.xml startup.app +install(FILES iai_kitchen.rosinstall jsk_pr2.rosinstall install_pr1040_description.sh jsk_pr2.machine plugin.xml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) diff --git a/jsk_pr2_robot/pr2_base_trajectory_action/CMakeLists.txt b/jsk_pr2_robot/pr2_base_trajectory_action/CMakeLists.txt index 048aa8ed00..43769001c5 100644 --- a/jsk_pr2_robot/pr2_base_trajectory_action/CMakeLists.txt +++ b/jsk_pr2_robot/pr2_base_trajectory_action/CMakeLists.txt @@ -28,7 +28,12 @@ add_executable(pr2_base_trajectory_action src/pr2_base_trajectory_action_controller_node.cpp) target_link_libraries(pr2_base_trajectory_action ${catkin_LIBRARIES} ${Boost_LIBRARIES}) -install(DIRECTORY config include launch +install(DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} + FILES_MATCHING PATTERN "*.h" +) + +install(DIRECTORY config launch test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) install(TARGETS pr2_base_trajectory_action diff --git a/jsk_robot_common/jsk_robot_startup/CMakeLists.txt b/jsk_robot_common/jsk_robot_startup/CMakeLists.txt index bf9d8a55cb..a38a661a86 100644 --- a/jsk_robot_common/jsk_robot_startup/CMakeLists.txt +++ b/jsk_robot_common/jsk_robot_startup/CMakeLists.txt @@ -138,18 +138,35 @@ if($ENV{ROS_DISTRO} STRGREATER "indigo") # quadruped_joystick_teleop.cpp uses c+ ) endif() -install(DIRECTORY lifelog util launch images config cfg +install(DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} + FILES_MATCHING PATTERN "*.h") + +install(DIRECTORY util launch images config cfg euslisp apps DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) + +install(FILES lifelog_nodelet_plugins.xml + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + file(GLOB_RECURSE SCRIPT_PROGRAMS lifelog/*) foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) else() - install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/lifelog) + endif() +endforeach() +file(GLOB_RECURSE SCRIPT_PROGRAMS scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts) endif() endforeach() + if(mongodb_store_FOUND) install(TARGETS jsk_robot_lifelog ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} diff --git a/jsk_robot_common/jsk_robot_startup/lifelog/mongodb_replication_params.yaml b/jsk_robot_common/jsk_robot_startup/lifelog/mongodb_replication_params.yaml index 966a90f87d..2a8b99f9ea 100644 --- a/jsk_robot_common/jsk_robot_startup/lifelog/mongodb_replication_params.yaml +++ b/jsk_robot_common/jsk_robot_startup/lifelog/mongodb_replication_params.yaml @@ -1,4 +1,4 @@ -mongodb_store_extras: [["robot-database.jsk.imi.i.u-tokyo.ac.jp", 27017],["musca.jsk.imi.i.u-tokyo.ac.jp",27017]] +mongodb_store_extras: [["robot-database.jsk.imi.i.u-tokyo.ac.jp", 27017]] replication_client: interval: 3600 delete_after_move: true diff --git a/jsk_robot_common/jsk_robot_startup/package.xml b/jsk_robot_common/jsk_robot_startup/package.xml index 8b1e644b85..c008fbdf44 100644 --- a/jsk_robot_common/jsk_robot_startup/package.xml +++ b/jsk_robot_common/jsk_robot_startup/package.xml @@ -19,6 +19,7 @@ sensor_msgs dynamic_reconfigure urdf + actionlib_msgs app_manager dynamic_reconfigure diff --git a/jsk_robot_common/jsk_robot_startup/scripts/head_teleop_joy_completion.py b/jsk_robot_common/jsk_robot_startup/scripts/head_teleop_joy_completion.py index cc323c7ee1..a1637a517a 100755 --- a/jsk_robot_common/jsk_robot_startup/scripts/head_teleop_joy_completion.py +++ b/jsk_robot_common/jsk_robot_startup/scripts/head_teleop_joy_completion.py @@ -58,9 +58,9 @@ def callback(self,msg): rospy.loginfo("raw: x={:5.2f}, y={:5.2f}, v={:5.2f}, r={:5.2f} -> cmd: x={:5.2f}, y={:5.2f}, r={:5.2f}".format(x, y, v, r, xx, yy, rr)) msg.axes = list(msg.axes) # tuple to list - msg.axes[self.axis_linear_x] = xx - msg.axes[self.axis_linear_y] = yy - msg.axes[self.axis_angular] = rr + msg.axes[self.axis_linear_x] = max(min(xx, self.axis_linear_x_max), self.axis_linear_x_min) + msg.axes[self.axis_linear_y] = max(min(yy, self.axis_linear_y_max), self.axis_linear_y_min) + msg.axes[self.axis_angular] = max(min(rr, self.axis_angular_max), self.axis_angular_min) self.pub.publish(msg) @@ -74,6 +74,13 @@ def spin(self): if rospy.Time.now() - self.last_publish_time > rospy.Duration(3.0): self.pub.publish(msg) + def get_param(self, key1, key2, value): + p = rospy.get_param(key1) + if type(p) == dict and p.has_key(key2): + return p[key2] + else: + return value + def __init__(self): rospy.init_node('joy_topic_completion') self.pass_through = rospy.get_param('~pass_through', True) @@ -81,6 +88,12 @@ def __init__(self): self.axis_linear_x = int(rospy.get_param('~axis_linear', {'x': 1})['x']) self.axis_linear_y = int(rospy.get_param('~axis_linear', {'y': 2})['y']) self.axis_angular = int(rospy.get_param('~axis_angular', {'yaw': 0})['yaw']) + self.axis_linear_x_min = float(self.get_param('~axis_linear', 'x_min', -1)) + self.axis_linear_x_max = float(self.get_param('~axis_linear', 'x_max', 1)) + self.axis_linear_y_min = float(self.get_param('~axis_linear', 'y_min', -1)) + self.axis_linear_y_max = float(self.get_param('~axis_linear', 'y_max', 1)) + self.axis_angular_min = float(self.get_param('~axis_angular', 'yaw_min', -0.5)) + self.axis_angular_max = float(self.get_param('~axis_angular', 'yaw_max', 0.5)) self.last_publish_time = rospy.Time.now() diff --git a/jsk_robot_common/jsk_robot_startup/src/quadruped_joystick_teleop.cpp b/jsk_robot_common/jsk_robot_startup/src/quadruped_joystick_teleop.cpp index 9a35c227a7..d0077eec62 100644 --- a/jsk_robot_common/jsk_robot_startup/src/quadruped_joystick_teleop.cpp +++ b/jsk_robot_common/jsk_robot_startup/src/quadruped_joystick_teleop.cpp @@ -67,7 +67,7 @@ class TeleopManager sound_play::SoundRequest msg; msg.sound = sound_play::SoundRequest::SAY; msg.command = sound_play::SoundRequest::PLAY_ONCE; - msg.volume = 1.0; + msg.volume = 0.3; msg.arg = message; pub_sound_play_.publish(msg); } @@ -92,7 +92,7 @@ class TeleopManager if ( client_estop_hard_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_estop_hard_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_estop_hard_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_estop_hard_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_estop_hard_] = true; } @@ -115,7 +115,7 @@ class TeleopManager if ( client_estop_gentle_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_estop_gentle_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_estop_gentle_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_estop_gentle_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_estop_gentle_] = true; } @@ -138,7 +138,7 @@ class TeleopManager if ( client_power_off_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_power_off_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_power_off_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_power_off_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_power_off_] = true; } @@ -161,7 +161,7 @@ class TeleopManager if ( client_power_on_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_power_on_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_power_on_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_power_on_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_power_on_] = true; } @@ -184,7 +184,7 @@ class TeleopManager if ( client_self_right_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_self_right_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_self_right_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_self_right_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_self_right_] = true; } @@ -207,7 +207,7 @@ class TeleopManager if ( client_sit_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_sit_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_sit_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_sit_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_sit_] = true; } @@ -230,7 +230,7 @@ class TeleopManager if ( client_stand_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_stand_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_stand_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_stand_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_stand_] = true; } @@ -253,7 +253,7 @@ class TeleopManager if ( client_stop_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_stop_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_stop_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_stop_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_stop_] = true; } @@ -276,7 +276,7 @@ class TeleopManager if ( client_release_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_release_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_release_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_release_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_release_] = true; } @@ -299,7 +299,7 @@ class TeleopManager if ( client_claim_.call(srv) && srv.response.success ) { ROS_INFO_STREAM("Service " << client_claim_.getService() << " succeeded."); } else { - ROS_ERROR_STREAM("Service " << client_claim_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_claim_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_claim_] = true; } @@ -328,7 +328,7 @@ class TeleopManager ROS_INFO_STREAM("Service " << client_stair_mode_.getService() << " succeeded. set to " << req_next_stair_mode_.data); req_next_stair_mode_.data = not req_next_stair_mode_.data; } else { - ROS_ERROR_STREAM("Service " << client_stair_mode_.getService() << " failed."); + ROS_ERROR_STREAM("Service " << client_stair_mode_.getService() << " failed. (" << srv.response.message << ")"); } pressed_[button_stair_mode_] = true; } @@ -350,7 +350,7 @@ class TeleopManager if (client_dock_.call(srv) && srv.response.success ){ ROS_INFO_STREAM("Service '" << client_dock_.getService() << "' succeeded."); } else { - ROS_ERROR_STREAM("Service '" << client_dock_.getService() << "' failed."); + ROS_ERROR_STREAM("Service '" << client_dock_.getService() << "' failed. (" << srv.response.message << ")"); } pressed_axes_[axe_dock_] = true; } @@ -358,9 +358,9 @@ class TeleopManager if (not pressed_axes_[axe_dock_]){ this->say("undock calling"); if (client_undock_.call(srv) && srv.response.success ){ - ROS_INFO("Service 'undock' succeeded."); + ROS_INFO_STREAM("Service '" << client_undock_.getService() << "' succeeded."); } else { - ROS_ERROR("Service 'undock' failed."); + ROS_ERROR_STREAM("Service '" << client_undock_.getService() << "' failed. (" << srv.response.message << ")"); } pressed_axes_[axe_dock_] = true; } @@ -384,7 +384,7 @@ class TeleopManager if (client_tuck_.call(srv) && srv.response.success ){ ROS_INFO_STREAM("Service '" << client_tuck_.getService() << "' succeeded."); } else { - ROS_ERROR_STREAM("Service '" << client_tuck_.getService() << "' failed."); + ROS_ERROR_STREAM("Service '" << client_tuck_.getService() << "' failed. (" << srv.response.message << ")"); } pressed_axes_[axe_tuck_] = true; } @@ -394,7 +394,7 @@ class TeleopManager if (client_untuck_.call(srv) && srv.response.success ){ ROS_INFO_STREAM("Service '" << client_untuck_.getService() << "' succeeded."); } else { - ROS_ERROR_STREAM("Service '" << client_untuck_.getService() << "' failed."); + ROS_ERROR_STREAM("Service '" << client_untuck_.getService() << "' failed. (" << srv.response.message << ")"); } pressed_axes_[axe_tuck_] = true; } diff --git a/jsk_robot_common/jsk_robot_utils/CMakeLists.txt b/jsk_robot_common/jsk_robot_utils/CMakeLists.txt index 5c80baa63b..acb7e736bf 100644 --- a/jsk_robot_common/jsk_robot_utils/CMakeLists.txt +++ b/jsk_robot_common/jsk_robot_utils/CMakeLists.txt @@ -10,7 +10,15 @@ if(CATKIN_ENABLE_TESTING) add_rostest(test/joint_state_compressor.test) endif() -install(DIRECTORY euslisp +file(GLOB_RECURSE SCRIPT_PROGRAMS scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts) + endif() +endforeach() +install(DIRECTORY euslisp test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS diff --git a/jsk_robot_common/speak_and_wait_recovery/CMakeLists.txt b/jsk_robot_common/speak_and_wait_recovery/CMakeLists.txt index 1e9db5e7f2..a29626d496 100644 --- a/jsk_robot_common/speak_and_wait_recovery/CMakeLists.txt +++ b/jsk_robot_common/speak_and_wait_recovery/CMakeLists.txt @@ -53,6 +53,9 @@ install(DIRECTORY include/${PROJECT_NAME}/ FILES_MATCHING PATTERN "*.h" ) +install(DIRECTORY launch test + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + install(FILES speak_and_wait_plugin.xml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} ) diff --git a/jsk_robot_common/update_move_base_parameter_recovery/CMakeLists.txt b/jsk_robot_common/update_move_base_parameter_recovery/CMakeLists.txt index c684447081..7e3eefc877 100644 --- a/jsk_robot_common/update_move_base_parameter_recovery/CMakeLists.txt +++ b/jsk_robot_common/update_move_base_parameter_recovery/CMakeLists.txt @@ -61,6 +61,9 @@ install(DIRECTORY include/${PROJECT_NAME}/ FILES_MATCHING PATTERN "*.h" ) +install(DIRECTORY config launch tests worlds + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + install(FILES update_move_base_parameter_recovery_plugins.xml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} ) diff --git a/jsk_spot_robot/README.md b/jsk_spot_robot/README.md new file mode 100644 index 0000000000..9f642f4988 --- /dev/null +++ b/jsk_spot_robot/README.md @@ -0,0 +1,122 @@ +jsk_spot_robot +============== + +## Manuals + +- [Supported Documents of Boston Dynamics](https://www.bostondynamics.com/spot/training/documentation) +- [Spot ROS User Documentation](http://www.clearpathrobotics.com/assets/guides/melodic/spot-ros/ros_usage.html#taking-control-of-the-robot) + +## Installation + +To setup a new internal PC and spot user, Please see [this page](./SetupInternalPCAndSpotUser.md). + +### How to set up a catkin workspace for a remote PC + +Create a workspace + +```bash +source /opt/ros/melodic/setup.bash +mkdir ~/spot_ws/src -p +cd ~/spot_ws/src +wstool init . +wstool set jsk-ros-pkg/jsk_robot https://github.com/k-okada/jsk_robot.git --git -v spot_arm +wstool update +wstool merge -t . jsk-ros-pkg/jsk_robot/jsk_spot_robot/jsk_spot_user.rosinstall +wstool update +rosdep update +rosdep install -y -r --from-paths . --ignore-src +cd $HOME/spot_ws +catkin init +catkin build -j4 jsk_spot_startup spoteus +``` + +## How to run + +### Bringup spot + +First, please turn on spot and turn on motors according to [the OPERATION section of spot user guide](https://www.bostondynamics.com/sites/default/files/inline-files/spot-user-guide.pdf) and power on the internal PC. + +Basically, ros systemd services will start automatically. So you can use spot now. + + +#### superviosur + +URI: http://:9001 + +#### rwt_app_chooser + +URI: http://:8000/rwt_app_chooser + +### Development with a remote PC + +Please create `spot_ws` to your PC. + +First, connect your development PC's wifi adapter to Access point of the robot. + +And for every terminals in this section, Set your ROS_IP and ROS_MASTER_URI and source spot_ws. + +``` +rossetmaster +rossetip +source ~/spot_ws/devel/setup.bash +``` + +##### spoteus + +spoteus is roseus client for ROS Interface of spot_ros. + +Open a terminal, setting up it for spot and run roseus repl. ( emacs shell or euslime are recommended. ) + +Then initialize it for spot + +``` +(load "package://spoteus/spot-interface.l") +(spot-init) +``` + +You can now control spot from roseus. +for example, when you want to move spot 1m forward, type. + +``` +(send *ri* :undock) ;; undock robot +(send *spot* :reset-pose) ;; change robot pose, use (objects (list *spot*)) to visuailze +(send *ri* :angle-vector (send *spot* :angle-vector) 2000 :default-controller) ;; send to real robot +(send *ri* :stow-arm) ;; contorl onbody-api +(send *ri* :sit) +``` + +### How to set up a catkin workspace in on-bodyPC + +First create user account into internal PC +``` +ssh spotcore -l spot +sudo adduser k-okada +sudo adduser k-okada spot +sudo adduser k-okada sudo +``` + +Create a workspace. Make sure that you need to login to spotcore with your account + +```bash +ssh spotcore -l k-okada +source /opt/ros/melodic/setup.bash +source ~spot/spot_driver_ws/devel/setup.bash +mkdir ~/spot_ws/src -p +cd ~/spot_ws/src +cd $HOME/spot_ws +catkin init +catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python3 -DPYTHON_INCLUDE_DIR=/usr/include/python3.6m -DPYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6m.so +catkin build -j4 jsk_spot_startup spoteus +echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc +echo "source ~/spot_ws/devel/setup.bash" >> ~/.bashrc +``` + +#### Run apps + +You can run apps manually, this is good for debugging your applications. +```bash +roscd jsk_spot_startup/apps/head_lead_demo roslaunch head_lead_demo.xml +``` + +#### Install apps +If you would like to call your apps from rwt_app_chooser, you can diff --git a/jsk_spot_robot/SetupInternalPCAndSpotUser.md b/jsk_spot_robot/SetupInternalPCAndSpotUser.md new file mode 100644 index 0000000000..230505513e --- /dev/null +++ b/jsk_spot_robot/SetupInternalPCAndSpotUser.md @@ -0,0 +1,307 @@ +# How to setup a internal PC and spot user + +This page describes how to setup a internal pc and spot user. + +## How to setup a [CORE I/O](https://dev.bostondynamics.com/docs/payload/coreio_documentation) + +The CORE I/O is desgined to use filesystem is read only and user Docekr or Spot Extensions for user applications. + + +### Setup core-io users + +login with default password (ex: ssh -p 20022 10.0.0.3 -el spot) + +``` +# better to reboot on every command ???? +core-io$ passwd ... # change password +core-io$ sudo usermod -a -G docker spot # add spot to docker group +``` + +# Build Docker containers for Development environment + +Software development on core-is uses Docker containers and we provided two containers. + +1. spot_dev_env +2. `_dev_env`. + +The `spot_dev_env` runs as `spot` user, which will provide basic functions like `roscore` and `jsk_spot_startup` and should be started at boot time. + +Users are expected to run `_dev_env` and create an overay workspace (https://wiki.ros.org/catkin/Tutorials/workspace_overlaying) on top of the workspace of `spot` user. + +## Build `spot_dev_env` (for admin) + +1. Login to spot as `spot` user or any ararch64 machine as user with uid `1000`. Using an aarch64 machine is recommended because `core-io` has less CPU power. +2. Create a ROS workspace in `~/spot_dev_env`. Then change to the `jsk_spot_robot/coreio/base` directory. +3. Run `make build`. This will build the docker image and compile all packages needed for `jsk_spot_startup`. + + +If you have built `spot_dev_env` on a machine other than `core-io, + +1. Run `docker save spot_dev_env:latest -o spot_dev_env.tar` on your aarch64 machine. +2. copy it to core-io, e.g. `scp spot_dev_env.tar spot@core-io:/tmp'. +3. Run `docker load -i /tmp/spot_dev_env.tar' on `core-io` machine. +4. As this docker image requires `~/spot_dev_env`. Please set up the ROS workspace under `~/spot_dev_env` as the same as your aarch64 machine. +5. Run `make build` in the `jsk_spot_robot/coreio/base` directory. + +## Build `_dev_env` + +1. Create your ROS workspace on `core-io` as your own userid. +2. go to `jsk_spot_robot/coreio/base` and run `make build`. + +# Run Docker container as your development environment + +## Run the startup program (for admin) + +1.Login to `core-io` as `spot` user, run `~/bash.sh` to start `spot_dev_env` container2. run `roslaunch jsk_spot_startup jsk_spot_bringup.launch credential_config:=$(rospack find jsk_spot_startup)/auth/spot_credential.yaml use_gps:=false`. + +Note: +- This process should be run as upstart or supervisor. +- Please do not start multiple `spot_dev_env` containers, it will restart multiple system services including bluetooth, so it will disconnect your joystick. + +## Run your custom development environment + +1. Login to `core-io` as your local user and run `~/bash.sh` to start your build environment. +2. For the first time, you don't have an overlay package, so if you run `rospack list | grep $HOME`, you will get nothing. If you have a package to modify, run `catkin ` and `source devel/setup.bash`, you will have your package on your overlay workspace. + + +# Tips + +### Setup development environment + +``` +core-io$ mkdir ~/spot_dev_env/src -p +core-io$ cd ~/spot_dev_env/src +core-io$ git clone https://github.com/k-okada/jsk_robot.git -b spot_arm +core-io$ cd ~/spot_dev_env/src/jsk_robot/jsk_spot_robot/coreio/base +core-io$ make build bash +``` +This procedure create `~/bash.sh`. Please use this script when you start development. + +### Setup wifi interfaces + +Use [TP-Link AC600 wireless Realtek RTL8811AU Archer T2U Nano](https://www.amazon.co.jp/gp/product/B07MXHJ6KB/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1) +``` +$ dmesg +[ 7.305827] usb 1-2.1.1: new high-speed USB device number 5 using tegra-xusb +[ 7.326404] usb 1-2.1.1: New USB device found, idVendor=2357, idProduct=011e +[ 7.326553] usb 1-2.1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[ 7.326681] usb 1-2.1.1: Product: 802.11ac WLAN Adapter +[ 7.326781] usb 1-2.1.1: Manufacturer: Realtek +$ lsusb +Bus 001 Device 005: ID 2357:011e +``` + +Make sure that the roming configuration works correctly (from core-io) +``` +core-io$ sudo wpa_cli get_network 0 bgscan +Selected interface 'wlxac15a246e382' +"simple:30:-80:86400" +``` +This scan every 30 seconds, when signal is below -80, and 86400 seconds (24 hours) otherwise. + +### Setup Joystick + +[Push PS and (left-top small) create button to enter pairing mode](https://www.playstation.com/en-us/support/hardware/pair-dualsense-controller-bluetooth/#blue) +``` +$ hcitool dev +Devices: + hci0 00:1B:DC:0D:D0:AD +$ hcitool -i hci0 scan +Scanning ... + D0:BC:C1:CB:48:37 Wireless Controller +$ sudo bluetoothctl +[bluetooth]# pair D0:BC:C1:CB:48:37 +[bluetooth]# trust D0:BC:C1:CB:48:37 +[bluetooth]# connect D0:BC:C1:CB:48:37 + +``` + + +## Settings for SPOT CORE (x86_64) + +## wifi settings [Wi-Fi roaming aggressiveness configuration](https://github.com/jsk-ros-pkg/jsk_robot/issues/1598#issuecomment-1247533330) + +``` +$ git clone https://github.com/cilynx/rtl88x2bu.git +cd rtl88x2bu +./deply.sh +echo 88x2bu | sudo tee /etc/modules-load.d/88x2bu.conf # to startup on boot time +echo 'install 88x2bu /sbin/modprobe -i 88x2bu && { /sbin/wpa_cli set_network 0 bgscan "\\"simple:5:-50:3000\\"";}' | sudo tee /etc/modprobe.d/88x2bu.conf # run set_network when load module + +sudo nmtui # to configure network +``` + +### Setup timezone + +``` +sudo timedatectl set-timezone Asia/Tokyo +``` + + +## How to set up the spot user + +First, create your user account to internal PC. + +``` +ssh spot-BD-xxxxxxxx-p.jsk.imi.i.u-tokyo.ac.jp -l spot -p 20022 +sudo adduser k-okada +sudo adduser k-okada spot + +``` + +If you are the first user to use spotcore, add `spot` user to sudo group. If someone already using the spotcore, you can skip this section + +``` +sudo gpasswd -a spot sudo +``` + +To install systemd service, run following commands. Note that this script start launch file of user's workspace, so usually we expect to run from spot users. +``` +rosrun robot_upstart install --provider supervisor --supervisor-priority 10 --roscore +rosrun robot_upstart install --provider supervisor --supervisor-priority 300 --symlink --wait --job jsk_spot_startup jsk_spot_startup/launch/jsk_spot_bringup.launch credential_config:=$(rospack find jsk_spot_startup)/auth/spot_credential.yaml use_app_manager:=false +rosrun robot_upstart install --provider supervisor --supervisor-priority 400 --symlink --wait --job app_manager jsk_robot_startup/lifelog/app_manager.launch use_applist:=false respawn:=false +``` + +To check output of roslaunch output, please try +``` +sudo supervisorctl tail -f jsk_spot_startup stdout +sudo supervisorctl tail -f jsk_spot_startup stderr +``` + +You can connect to the supervisor console from https://spotcore.jsk.imi.i.u-tokyo.ac.jp:9001/ + +systemd services of JSK Spot system use workspaces in `spot` user. + +- `spot_driver_ws`: a workspace to run driver.launch. which requires python3 build version of geometry3 + +### Prerequities + +Install necessary packages for workspace building + +``` +sudo apt-get install python3-catkin-pkg-modules python3-rospkg-modules python3-venv python3-empy python3-opencv python3-sip-dev python3-defusedxml +sudo apt-get install ros-melodic-catkin ros-melodic-vision-msgs +``` + + +### setting up `spot_driver_ws` + +Create a workspace for `spot_driver`. + +```bash +source /opt/ros/melodic/setup.bash +mkdir ~/spot_driver_ws/src -p +cd ~/spot_driver_ws/src +wstool init . +wstool set jsk-ros-pkg/jsk_robot https://github.com/jsk-ros-pkg/jsk_robot.git --git +wstool update +wstool merge -t . jsk-ros-pkg/jsk_robot/jsk_spot_robot/jsk_spot_driver.rosinstall +wstool update +rosdep update +rosdep install -y -r --from-paths . --ignore-src +pip3 install -r jsk-ros-pkg/jsk_robot/jsk_spot_robot/requirements.txt +cd ~/spot_driver_ws +catkin init +catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python3 -DPYTHON_INCLUDE_DIR=/usr/include/python3.6m -DPYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6m.so +catkin build -j4 tf2_ros cv_bridge jsk_spot_startup spoteus robot_upstart +``` + +After this, please modify the credential files for spot_driver. + +```bash +roscd jsk_spot_startup +# modify auth/credential_config.yaml +git update-index --skip-worktree auth/spot_credential.yaml +``` + +Note that `rosdep install ...` did not install all dependencies, you need to install `python3-` modules manualy. or run `ROS_PYTHON_VERSION=3 rosdep install ...`. But this will install `python3-catkin-pkg` which remove `python2-catkin-pkg` package and all `ros-melodic-*` packages. + +Workaround is to install only jsk_robot direcotry. (But you still re-install `ros-melodic-jsk-tools`) +``` +ROS_PYTHON_VERSION=3 rosdep install -r --from-paths ~/spot_driver_ws/src/jsk_robot --ignore-src +``` +or create dummy package that did not conflict each other. + +``` +#!/bin/bash + +set -x +set -e + +TMPDIR=/tmp/tmp-$$ + +mkdir ${TMPDIR} +cd ${TMPDIR} +apt download python3-catkin-pkg +dpkg-deb -R python3-catkin-pkg_0.5.2-100_all.deb ${TMPDIR}/src-python3-catkin-pkg +rm -fr ${TMPDIR}/src-python3-catkin-pkg/usr/bin/ +sed -i /^Conflicts:/d ${TMPDIR}/src-python3-catkin-pkg/DEBIAN/control +sed -i '/^Version:/ s/$/0/' ${TMPDIR}/src-python3-catkin-pkg/DEBIAN/control +cat ${TMPDIR}/src-python3-catkin-pkg/DEBIAN/control +# edit DEBIAN/postinst +dpkg-deb -b ${TMPDIR}/src-python3-catkin-pkg python3-catkin-pkg-dummy.deb + +apt download python-catkin-pkg +dpkg-deb -R python-catkin-pkg_0.5.2-100_all.deb ${TMPDIR}/src-python2-catkin-pkg +rm -fr ${TMPDIR}/src-python2-catkin-pkg/usr/bin/ +sed -i 's/, python3-catkin-pkg//' ${TMPDIR}/src-python2-catkin-pkg/DEBIAN/control +sed -i '/^Version:/ s/$/0/' ${TMPDIR}/src-python2-catkin-pkg/DEBIAN/control +cat ${TMPDIR}/src-python2-catkin-pkg/DEBIAN/control +# edit DEBIAN/postinst +dpkg-deb -b ${TMPDIR}/src-python2-catkin-pkg python2-catkin-pkg-no-conflict.deb + +apt download python3-rosdep +dpkg-deb -R python3-rosdep_0.22.1-1_all.deb ${TMPDIR}/src-python3-rosdep +rm -fr ${TMPDIR}/src-python3-rosdep/usr/bin/ +sed -i /^Conflicts:/d ${TMPDIR}/src-python3-rosdep/DEBIAN/control +sed -i '/^Version:/ s/$/0/' ${TMPDIR}/src-python3-rosdep/DEBIAN/control +cat ${TMPDIR}/src-python3-rosdep/DEBIAN/control +# edit DEBIAN/postinst +dpkg-deb -b ${TMPDIR}/src-python3-rosdep python3-rosdep-dummy.deb + +apt download python-rosdep +dpkg-deb -R python-rosdep_0.22.1-1_all.deb ${TMPDIR}/src-python-rosdep +rm -fr ${TMPDIR}/src-python-rosdep/usr/bin/ +sed -i 's/, python3-rosdep//' ${TMPDIR}/src-python-rosdep/DEBIAN/control +sed -i '/^Version:/ s/$/0/' ${TMPDIR}/src-python-rosdep/DEBIAN/control +cat ${TMPDIR}/src-python-rosdep/DEBIAN/control +# edit DEBIAN/postinst +dpkg-deb -b ${TMPDIR}/src-python-rosdep python-rosdep-no-conflict.deb + +set +x +echo "scp ${TMPDIR}/python2-catkin-pkg-no-conflict.deb to your environment" +echo "scp ${TMPDIR}/python3-catkin-pkg-dummy.deb to your environment" +echo "scp ${TMPDIR}/python-rosdep-no-conflict.deb to your environment" +echo "scp ${TMPDIR}/python3-rosdep-dummy.deb to your environment" +``` + +## Troubleshootig + +### Joystick did not respond + +Check `Setup Joystick` section and reconnet bluetooth + +### Wifi did not connec + +Check [roaming aggressiveness configuration](https://github.com/jsk-ros-pkg/jsk_robot/issues/1598) +``` +$ wpa_cli get_network 0 bgscan +``` + +This tells roaming configuation, for example below setting means it scan every 5 seconds when the signal is weak (below -50), and every 3600 seconds otherwise. +``` +$ wpa_cli set_network 0 bgscan "\"simple:5:-50:3000\"" +``` + +You can also check the output of wpa_supplicant. +``` +$ journalctl -u wpa_supplicant -f +``` + +### rwt_app_chooser did not respond + +http://spotcore:8000/rwt_app_chooser/#!task/ did not show any apps, and following command did not returns any apps, make sure that you have run `rosdep update`. + +``` +$ rosservice call /SpotCORE/list_apps +``` diff --git a/jsk_spot_robot/coreio/base/Dockerfile b/jsk_spot_robot/coreio/base/Dockerfile new file mode 100644 index 0000000000..be53b0674f --- /dev/null +++ b/jsk_spot_robot/coreio/base/Dockerfile @@ -0,0 +1,121 @@ +##### +## Setup development environment +##### +##### Use sha256 as of 2023/03/16 +FROM arm64v8/ros:melodic-robot@sha256:c0a0a8a25dc822ea48b40c45d74b267ddf4dbe22b8accc9fd66bb146c34eb974 AS base_build +ENV DEBIAN_FRONTEND noninteractive + +# development tools +RUN apt-get update && apt install -y -q python3-catkin-pkg-modules python3-rospkg-modules python3-venv python3-pip python3-empy python3-opencv python3-sip-dev python3-defusedxml cython3 + +# network tools +RUN apt-get update && apt install -y -q net-tools iputils-ping dnsutils traceroute curl + +# development tools +RUN apt-get update && apt install -y -q python-catkin-tools ros-melodic-catkin-virtualenv + +# editors +RUN apt-get update && apt install -y -q less emacs vim + +# bluetooth +RUN apt-get update && apt install -y -q bluez bluez-tools + +# ROS packages +RUN apt-get update && apt install -y -q ros-melodic-catkin ros-melodic-vision-msgs + +# setup user space +RUN sed -i 's@%sudo\s*ALL=(ALL:ALL)\s*ALL@%sudo ALL=(ALL) NOPASSWD: ALL@' /etc/sudoers +RUN cat /etc/sudoers +RUN useradd -ms /bin/bash spot +RUN echo spot:spot | chpasswd +RUN adduser spot sudo +USER spot +WORKDIR /home/spot +RUN rosdep update -y --include-eol-distros + +# get dummy workspace to install dpeendent packages +# setup catkin develeopment environment with mounted workspace +RUN mkdir -p /tmp/ws +COPY package.tar /tmp/ +RUN tar -C /tmp/ws/ -xvf /tmp/package.tar + +# install pip3 dependencies +RUN pip3 install pip==21.3.1 +RUN pip3 install wrapt==1.12.1 six==1.15.0 PyJWT==2.0.0 numpy==1.19.4 grpcio==1.34.0 + +# install python requirements +RUN pip3 install -r /tmp/ws/src/jsk_robot/jsk_spot_robot/requirements.txt + +# # rosdep install +# RUN sudo apt-get update && rosdep install -y -r --from-paths /tmp/ws/src --ignore-src --rosdistro melodic || echo "OK" + +##### +## Initialize catkin workspace after wstool merge and update with jsk_spot_robot/jsk_spot_driver.rosinstall +##### +FROM base_build AS pre_build + +# setup catkin develeopment environment with mounted workspace +COPY package.tar /tmp/ +RUN tar -C /tmp/ws/ -xvf /tmp/package.tar +RUN sudo apt-get update && rosdep install -y -r --from-paths /tmp/ --ignore-src --rosdistro melodic || echo "OK" +# rosdep install for python3 +RUN sudo apt-get update && ROS_PYTHON_VERSION=3 rosdep install -y -r --skip-keys="python3-catkin-pkg python3-catkin-tools python3-rosinstall python3-rosdep" --from-paths /tmp/ --ignore-src --rosdistro melodic || echo "OK" + +## +## 'RUN --mount' called everytime when conttnes of spot/ws changed, so use 'COPY' +#RUN --mount=type=bind,source=/,target=/home/spot/ws cd ws && rosdep install -y -r --from-paths src --ignore-src --rosdistro melodic || echo "OK" + + +##### +## User preferences for developent tools +##### +FROM pre_build AS dev_build + +RUN echo "#!/bin/bash" > /home/spot/ros_entrypoint.sh && \ + echo "set -e" >> /home/spot/ros_entrypoint.sh && \ + echo "source /opt/ros/melodic/setup.bash --" >> /home/spot/ros_entrypoint.sh && \ + echo "[ -e /home/spot/ws/install/setup.bash ] && source /home/spot/ws/install/setup.bash" >> /home/spot/ros_entrypoint.sh && \ + echo "sudo service dbus start" >> /home/spot/ros_entrypoint.sh && \ + echo "sudo bluetoothd &" >> /home/spot/ros_entrypoint.sh && \ + echo "exec \"\$@\"" >> /home/spot/ros_entrypoint.sh + +RUN chmod 0777 /home/spot/ros_entrypoint.sh + +### FIXME ros_entrypoint did not work with roscd +RUN echo "source /opt/ros/melodic/share/rosbash/rosbash" >> ~/.bashrc + +ENV TZ=Asia/Tokyo + +ENTRYPOINT ["/home/spot/ros_entrypoint.sh"] +CMD ["bash"] + +FROM dev_build AS user_build + +ARG USER +ENV USER ${USER:-spot} +ARG UID +ENV UID ${UID:-1000} +ARG GID +ENV GID ${GID:-1000} +RUN sudo addgroup --gid ${GID} ${USER} +RUN sudo adduser --disabled-password --gecos '' --uid ${UID} --gid ${GID} ${USER} +RUN sudo adduser ${USER} sudo +USER ${USER} + +RUN echo "#!/bin/bash" > /home/${USER}/ros_entrypoint.sh && \ + echo "set -e" >> /home/${USER}/ros_entrypoint.sh && \ + echo "source /opt/ros/melodic/setup.bash --" >> /home/${USER}/ros_entrypoint.sh && \ + echo "source /home/spot/ws/install/setup.bash --" >> /home/${USER}/ros_entrypoint.sh && \ + echo "[ -e /home/${USER}/ws/devel/setup.bash ] && source /home/${USER}/ws/devel/setup.bash --" >> /home/${USER}/ros_entrypoint.sh && \ + echo "exec \"\$@\"" >> /home/${USER}/ros_entrypoint.sh + +RUN chmod 0777 /home/${USER}/ros_entrypoint.sh + +RUN echo "source /opt/ros/melodic/share/rosbash/rosbash" >> ~/.bashrc + +## add your favorite package +RUN sudo apt install -y clisp + +## FIXME @@HOME@@ is replaced by create_src_tree_tar in Makefile +ENTRYPOINT ["@@HOME@@/ros_entrypoint.sh"] +CMD ["bash"] diff --git a/jsk_spot_robot/coreio/base/Makefile b/jsk_spot_robot/coreio/base/Makefile new file mode 100644 index 0000000000..fe107c6e32 --- /dev/null +++ b/jsk_spot_robot/coreio/base/Makefile @@ -0,0 +1,128 @@ +HOME=$(shell realpath ~/) + +all: $(HOME)/bash.sh + @echo "make build" + +$(HOME)/bash.sh: + echo "#!/bin/bash\n\ncd $(CURDIR)\nmake shell\n" > $(HOME)/bash.sh + chmod u+x $(HOME)/bash.sh + + +WS_ROOT=$(abspath $(CURDIR)/../../../../../) + +HOSTNAME=$(shell hostname) +IPADDRESS=$(shell dig +short $(HOSTNAME).jsk.imi.i.u-tokyo.ac.jp) +IPADDRESS=$(shell hostname -I | sed 's/ /\n/g' | awk '/133.11/') + +ifeq ($(shell id -u $$USER),1000) ## This is dev(uid=1000) setting +DOCKER_USER=spot +DOCKER_TARGET_NAME=spot_dev_env +$(info "This is dev setting, create $(DOCKER_TARGET_NAME)") +ifneq ($(WS_ROOT),$(HOME)/spot_dev_env) +$(info "We assume WS_ROOT to $(HOME)/spot_dev_env but current WS_ROOT is '$(WS_ROOT)'. Please setup as follows") +$(info "$ mkdir -p ~/spot_dev_env/src") +$(info "$ cd ~/spot_dev_env/src") +$(info "$ git clone $(shell git ls-remote --get-url) -b $(shell git branch | grep ^* | sed s/\*\ // )") +$(error "") +endif +else +DOCKER_USER=$(USER) +DOCKER_TARGET_NAME=$(USER)_dev_env +$(info "This is user setting, create $(DOCKER_TARGET_NAME)") +endif + +define run_uid_not_equal +ifeq ($(shell id -u $$USER),${1}) +$$(info "${2}") +$$(error "Target '$@' need to run except uid ${1} ($(shell id -un ${1})), your current uid is $(shell id -u $$USER) ($(shell echo $$USER))") +endif +endef +define run_uid_equal +ifneq ($(shell id -u $$USER),${1}) +$$(info "${2}") +$$(error "Target '$@' need to run with uid ${1} ($(shell id -un ${1})), your current uid is $(shell id -u $$USER) ($(shell echo $$USER))") +endif +endef + +define run +docker run --rm --privileged --hostname $(HOSTNAME)-core-io --add-host $(HOSTNAME)-core-io:$(IPADDRESS) --add-host $(HOSTNAME)-core-io.jsk.imi.i.u-tokyo.ac.jp:$(IPADDRESS) --network=host -u=$(shell id -u $$USER):$(shell id -g $$USER) --group-add sudo --device=/dev/input -v /home/$(shell id -un 1000)/spot_dev_env:/home/spot/ws -v $(WS_ROOT):/home/$(DOCKER_USER)/ws -v $(HOME)/.ros:/home/$(DOCKER_USER)/.ros -v $(HOME)/.config:/home/$(DOCKER_USER)/.config -w /home/$(DOCKER_USER)/ws -ti $(DOCKER_TARGET_NAME) ${1}; +endef + +define create_src_tree_tar +$(info "Create source tree tar file") +$(eval TMP_FILE:=$(shell mktemp)) +$(eval PKG_FILE:=$(WS_ROOT)/package.tar) +cp Dockerfile $(WS_ROOT); sed -i 's+@@HOME@@+$(HOME)+' $(WS_ROOT)/Dockerfile; (cd $(WS_ROOT); find src -iname 'package.xml' -o -path '*jsk_spot_robot/requirements.txt' | tar cf $(TMP_FILE) -T -); chmod 644 $(TMP_FILE) +if test "$$(md5sum $(TMP_FILE) | awk '{print $$1}')" = "$$(md5sum $(PKG_FILE) | awk '{print $$1}')" ; then echo "- We have latest $(PKG_FILE)"; else echo "- source tree is different from $(PKG_FILE), create new source tree tar"; cp $(TMP_FILE) $(PKG_FILE); tar -tf $(PKG_FILE); fi +rm $(TMP_FILE) +endef +define delete_src_tree_tar +rm $(WS_ROOT)/Dockerfile $(PKG_FILE); +endef + +# setup workspace ( clone source code from jsk_spot_draiver.rosinstall, rosdep install, wstool ) +base_build: + $(eval $(call run_uid_equal,1000,This target requires dev setting with uid 1000)) + # make .ros file + mkdir -p ~/.ros + # create tar file to pass package.xml under jsk_spot_robot, so that pip3 install requirements.txt + $(call create_src_tree_tar) + # build base file + cd $(WS_ROOT); DOCKER_BUILDKIT=1 docker build --target base_build --progress=plain --network=host -t spot_dev_env -f Dockerfile . + # run rosdep update + $(call run, bash -c '[ -e /etc/ros/rosdep/sources.list.d/ ] || rosdep update -y --include-eol-distros') + # create workspace from jsk_spot_driver.rosinstall + if [ ! -e $(WS_ROOT)/src/.rosinstall ]; then $(call run, wstool init src) fi + $(call run, wstool merge -t src src/jsk_robot/jsk_spot_robot/jsk_spot_driver.rosinstall) + $(call run, wstool update -t src) + # delete package.tar + $(call delete_src_tree_tar) + +# run rosdep install +pre_build: base_build + $(eval $(call run_uid_equal,1000,This target requires dev setting with uid 1000)) + # create tarfile to pass package.xm under $($WS_ROOT), so that rosdep install finds necessary dependency list + $(call create_src_tree_tar) + # rosdep install + cd $(WS_ROOT); DOCKER_BUILDKIT=1 docker build --target pre_build --progress=plain --network=host -t spot_dev_env -f Dockerfile . + # delete package.tar + $(call delete_src_tree_tar) + +# install dev tools, ros_entrypoint.sh +dev_build: pre_build + $(eval $(call run_uid_equal,1000,This target requires dev setting with uid 1000)) + # dev_build does not use package.xml + $(call create_src_tree_tar) + cd $(WS_ROOT); DOCKER_BUILDKIT=1 docker build --target dev_build --progress=plain --network=host -t spot_dev_env -f Dockerfile . + # delete package.tar + $(call delete_src_tree_tar) + +# run user build +user_build: + $(eval $(call run_uid_not_equal,1000,This target requires user setting, except uid 1000)) + $(call create_src_tree_tar) + cd $(WS_ROOT); DOCKER_BUILDKIT=1 docker build --target user_build --progress=plain --network=host -t $(shell echo $$USER)_dev_env --build-arg USER=$$USER --build-arg UID=$(shell id -u $$USER) --build-arg GID=$(shell id -g $$USER) -f Dockerfile . + +# run catkin build +catkin_config: + $(call run, catkin init) + $(call run, catkin config $(CATKIN_CONFIG) --cmake-args -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python3 -DPYTHON_INCLUDE_DIR=/usr/include/python3.6m -DPYTHON_LIBRARY=/usr/lib/aarch64-linux-gnu/libpython3.6m.so.1) + +catkin_build: + $(call run, catkin build) + +# run all build +build: +ifeq ($(shell id -u $$USER),1000) ## This is dev(uid=1000) setting + make dev_build + make catkin_config CATKIN_CONFIG='--merge-install --blacklist test_tf2' + make catkin_build +else + make user_build + make catkin_config +endif +shell: + $(call run, bash) + +emacs: + $(call run, emacs -nw) diff --git a/jsk_spot_robot/coreio/network_compute_server/0_setup.sh b/jsk_spot_robot/coreio/network_compute_server/0_setup.sh new file mode 100755 index 0000000000..4b555ad992 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/0_setup.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +## see https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch1 + +. ~/my_spot_env/bin/activate diff --git a/jsk_spot_robot/coreio/network_compute_server/1_capture.sh b/jsk_spot_robot/coreio/network_compute_server/1_capture.sh new file mode 100755 index 0000000000..e96128ac60 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/1_capture.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +## see https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch1 + +set -x +rm -fr dogtoy/images/* + +trap "exit" INT TERM ERR +trap "kill 0" EXIT + +python capture_images.py 10.0.0.3 --image-source frontleft_fisheye_image --folder dogtoy/images & +python capture_images.py 10.0.0.3 --image-source frontright_fisheye_image --folder dogtoy/images & +python capture_images.py 10.0.0.3 --image-source left_fisheye_image --folder dogtoy/images & +python capture_images.py 10.0.0.3 --image-source right_fisheye_image --folder dogtoy/images & +python capture_images.py 10.0.0.3 --image-source back_fisheye_image --folder dogtoy/images & + +wait diff --git a/jsk_spot_robot/coreio/network_compute_server/2_split.sh b/jsk_spot_robot/coreio/network_compute_server/2_split.sh new file mode 100755 index 0000000000..201830d8b1 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/2_split.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +mv dogtoy/images/*.xml dogtoy/annotations/ +python split_dataset.py --labels-dir dogtoy/annotations/ --output-dir dogtoy/annotations/ --ratio 0.9 diff --git a/jsk_spot_robot/coreio/network_compute_server/3_convert.sh b/jsk_spot_robot/coreio/network_compute_server/3_convert.sh new file mode 100755 index 0000000000..155949ff77 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/3_convert.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +## see https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch1 + +python generate_tfrecord.py --xml_dir dogtoy/annotations/train --image_dir dogtoy/images --labels_path dogtoy/annotations/label_map.pbtxt --output_path dogtoy/annotations/train.record +python generate_tfrecord.py --xml_dir dogtoy/annotations/test --image_dir dogtoy/images --labels_path dogtoy/annotations/label_map.pbtxt --output_path dogtoy/annotations/test.record + diff --git a/jsk_spot_robot/coreio/network_compute_server/4_train.sh b/jsk_spot_robot/coreio/network_compute_server/4_train.sh new file mode 100755 index 0000000000..ece266b850 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/4_train.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +## see https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch1 + +python model_main_tf2.py --model_dir=dogtoy/models/dogtoy_ssd_resnet50_v1_fpn --pipeline_config_path=dogtoy/models/dogtoy_ssd_resnet50_v1_fpn/pipeline.config --num_train_steps=20000 +# CUDA_VISIBLE_DEVICES="-1" python model_main_tf2.py --model_dir=dogtoy/models/dogtoy_ssd_resnet50_v1_fpn --pipeline_config_path=dogtoy/models/dogtoy_ssd_resnet50_v1_fpn/pipeline.config --checkpoint_dir=dogtoy/models/dogtoy_ssd_resnet50_v1_fpn +mkdir -p dogtoy/exported-models/dogtoy-model +python exporter_main_v2.py --input_type image_tensor --pipeline_config_path dogtoy/models/dogtoy_ssd_resnet50_v1_fpn/pipeline.config --trained_checkpoint_dir dogtoy/models/dogtoy_ssd_resnet50_v1_fpn/ --output_directory dogtoy/exported-models/dogtoy-model diff --git a/jsk_spot_robot/coreio/network_compute_server/5_run.sh b/jsk_spot_robot/coreio/network_compute_server/5_run.sh new file mode 100755 index 0000000000..03b51fd747 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/5_run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +## see https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch1 + +python network_compute_server.py -m dogtoy/exported-models/dogtoy-model/saved_model dogtoy/annotations/label_map.pbtxt 10.0.0.3 + diff --git a/jsk_spot_robot/coreio/network_compute_server/6_build_ext.sh b/jsk_spot_robot/coreio/network_compute_server/6_build_ext.sh new file mode 100755 index 0000000000..ed260c2d1b --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/6_build_ext.sh @@ -0,0 +1,31 @@ +#!/bin/bash -ex + +## https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch6 +## https://dev.bostondynamics.com/docs/python/fetch_tutorial/files/coreio_extension/create_extension.sh + +mkdir -p data +rm -rf data/* +# Dogtoy model +cp -r ./dogtoy/exported-models/dogtoy-model data/. +# and its label map +cp ./dogtoy/annotations/label_map.pbtxt data/dogtoy-model/. +# coco model (includes its label map) +cp -r ./dogtoy/pre-trained-models/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8 data/. +cp ./models-with-protos/research/object_detection/data/mscoco_label_map.pbtxt data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/ + +# Build the image +docker build -t object_detection_coco:l4t -f Dockerfile.l4t . + +# Exports the image, uses pigz +docker save object_detection_coco:l4t | pigz > object_detection_coco_image.tar.gz + +# Built the Spot Extension by taring all the files together +tar -cvzf object_detection_coco.spx \ + object_detection_coco_image.tar.gz \ + manifest.json \ + docker-compose.yml \ + data + +# Cleanup intermediate image +rm object_detection_coco_image.tar.gz +rm -fr data diff --git a/jsk_spot_robot/coreio/network_compute_server/Dockerfile.l4t b/jsk_spot_robot/coreio/network_compute_server/Dockerfile.l4t new file mode 100644 index 0000000000..f5ba4ae1bc --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/Dockerfile.l4t @@ -0,0 +1,22 @@ +# Use a base image provided by nvidia that already contains tensorflow 2.5 +FROM nvcr.io/nvidia/l4t-tensorflow:r32.7.1-tf2.7-py3 + +# Do some basic apt and pip updating +RUN apt-get update && \ + apt-get install -y --no-install-recommends python3-pip && \ + apt-get clean + +# Copy over the python requirements file and our prebuilt models API library +COPY docker-requirements.txt prebuilt/*.whl ./ +COPY models-with-protos models-with-protos + +# Install the python requirements +RUN python3 -m pip install pip==21.3.1 setuptools==59.6.0 wheel==0.37.1 && \ + python3 -m pip install -r docker-requirements.txt --find-links . + +# Copy over our main script +COPY network_compute_server.py /app/ +WORKDIR /app + +# Set our script as the main entrypoint for the container +ENTRYPOINT ["python3", "network_compute_server.py"] diff --git a/jsk_spot_robot/coreio/network_compute_server/docker-compose.yml b/jsk_spot_robot/coreio/network_compute_server/docker-compose.yml new file mode 100644 index 0000000000..72a9437ea1 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.5" +services: + object_detection_coco: + image: object_detection_coco:l4t + network_mode: host + restart: unless-stopped + environment: + # This package couldn't be installed, but putting it on the path allows required access to the protos. + - PYTHONPATH=/models-with-protos/research/ + volumes: + # Mount payload credentials. + - /opt/payload_credentials/payload_guid_and_secret:/opt/payload_credentials/payload_guid_and_secret + # and ML models + - /data/.extensions/object_detection_coco/data:/data + # The command below is partial because the docker image is already configured with an entrypoint. + command: > + -m /data/dogtoy-model/saved_model /data/dogtoy-model/label_map.pbtxt + -m /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/saved_model /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/mscoco_label_map.pbtxt + --payload-credentials-file /opt/payload_credentials/payload_guid_and_secret + 192.168.50.3 + deploy: + resources: + reservations: + devices: + - driver: nvidia + capabilities: [gpu] diff --git a/jsk_spot_robot/coreio/network_compute_server/docker-requirements.txt b/jsk_spot_robot/coreio/network_compute_server/docker-requirements.txt new file mode 100644 index 0000000000..12c96e14fe --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/docker-requirements.txt @@ -0,0 +1,40 @@ +bosdyn-api==3.2.0 + # via + # bosdyn-client + # bosdyn-core +bosdyn-client==3.2.0 + # via -r requirements.txt +bosdyn-core==3.2.0 + # via bosdyn-client +certifi==2022.5.18.1 + # via requests +charset-normalizer==2.0.12 + # via requests +deprecated==1.2.13 + # via + # bosdyn-client + # bosdyn-core +grpcio + #==1.46.3 (conflicts with base image) + # via bosdyn-client +idna==3.3 + # via requests +numpy==1.19.4 + # via bosdyn-client +protobuf==3.19.4 + # via bosdyn-api +pyjwt==2.4.0 + # via bosdyn-client +requests==2.27.1 + # via bosdyn-client +six + #==1.16.0 (conflicts with base image) + # via grpcio +urllib3==1.26.9 + # via requests +wrapt + #==1.14.1 (conflicts with base image) + # via deprecated +Pillow==8.4.0 +opencv-python==4.6.0.66 + # via network_compute_server.py \ No newline at end of file diff --git a/jsk_spot_robot/coreio/network_compute_server/manifest.json b/jsk_spot_robot/coreio/network_compute_server/manifest.json new file mode 100644 index 0000000000..d762e573d0 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/manifest.json @@ -0,0 +1,7 @@ +{ + "description": "object_detection_coco", + "version": "3.2.0", + "images": [ + "object_detection_coco_image.tar.gz" + ] +} diff --git a/jsk_spot_robot/coreio/network_compute_server/network_compute_server.py b/jsk_spot_robot/coreio/network_compute_server/network_compute_server.py new file mode 100644 index 0000000000..d600d51577 --- /dev/null +++ b/jsk_spot_robot/coreio/network_compute_server/network_compute_server.py @@ -0,0 +1,312 @@ +# Copyright (c) 2022 Boston Dynamics, Inc. All rights reserved. +# +# Downloading, reproducing, distributing or otherwise using the SDK Software +# is subject to the terms and conditions of the Boston Dynamics Software +# Development Kit License (20191101-BDSDK-SL). + +import argparse +import io +import os +import sys +import time +import logging + +import cv2 +from PIL import Image +import numpy as np + +from bosdyn.api import network_compute_bridge_service_pb2_grpc +from bosdyn.api import network_compute_bridge_pb2 +from bosdyn.api import image_pb2 +from bosdyn.api import header_pb2 +import bosdyn.client +import bosdyn.client.util +import grpc +from concurrent import futures +import tensorflow as tf + +import queue +import threading +from google.protobuf import wrappers_pb2 +from object_detection.utils import label_map_util + +kServiceAuthority = "object-detection-coco-worker.spot.robot" + + +class TensorFlowObjectDetectionModel: + def __init__(self, model_path, label_path): + self.detect_fn = tf.saved_model.load(model_path) + self.category_index = label_map_util.create_category_index_from_labelmap(label_path, use_display_name=True) + self.name = os.path.basename(os.path.dirname(model_path)) + + def predict(self, image): + input_tensor = tf.convert_to_tensor(image) + input_tensor = input_tensor[tf.newaxis, ...] + detections = self.detect_fn(input_tensor) + + return detections + +def process_thread(args, request_queue, response_queue): + # Load the model(s) + models = {} + for model in args.model: + this_model = TensorFlowObjectDetectionModel(model[0], model[1]) + models[this_model.name] = this_model + + print('') + print('Service ' + args.name + ' running on port: ' + str(args.port)) + + print('Loaded models:') + for model_name in models: + print(' ' + model_name) + + while True: + request = request_queue.get() + + if isinstance(request, network_compute_bridge_pb2.ListAvailableModelsRequest): + out_proto = network_compute_bridge_pb2.ListAvailableModelsResponse() + for model_name in models: + out_proto.available_models.append(model_name) + if models[model_name].category_index is not None: + labels_msg = out_proto.labels.add() + labels_msg.model_name = model_name + for n in models[model_name].category_index.keys(): + labels_msg.available_labels.append(models[model_name].category_index[n]['name']) + response_queue.put(out_proto) + continue + else: + out_proto = network_compute_bridge_pb2.NetworkComputeResponse() + + # Find the model + if request.input_data.model_name not in models: + err_str = 'Cannot find model "' + request.input_data.model_name + '" in loaded models.' + print(err_str) + + # Set the error in the header. + out_proto.header.error.code = header_pb2.CommonError.CODE_INVALID_REQUEST + out_proto.header.error.message = err_str + response_queue.put(out_proto) + continue + + model = models[request.input_data.model_name] + + # Unpack the incoming image. + if request.input_data.image.format == image_pb2.Image.FORMAT_RAW: + pil_image = Image.open(io.BytesIO(request.input_data.image.data)) + if request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U8: + # If the input image is grayscale, convert it to RGB. + image = cv2.cvtColor(pil_image, cv2.COLOR_GRAY2RGB) + + elif request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_RGB_U8: + # Already an RGB image. + image = pil_image + + else: + print('Error: image input in unsupported pixel format: ', request.input_data.image.pixel_format) + response_queue.put(out_proto) + continue + + elif request.input_data.image.format == image_pb2.Image.FORMAT_JPEG: + dtype = np.uint8 + jpg = np.frombuffer(request.input_data.image.data, dtype=dtype) + image = cv2.imdecode(jpg, -1) + + if len(image.shape) < 3: + # If the input image is grayscale, convert it to RGB. + image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) + + image_width = image.shape[0] + image_height = image.shape[1] + + detections = model.predict(image) + + num_objects = 0 + + # All outputs are batches of tensors. + # Convert to numpy arrays, and take index [0] to remove the batch dimension. + # We're only interested in the first num_detections. + num_detections = int(detections.pop('num_detections')) + detections = {key: value[0, :num_detections].numpy() + for key, value in detections.items()} + + boxes = detections['detection_boxes'] + classes = detections['detection_classes'] + scores = detections['detection_scores'] + + + for i in range(boxes.shape[0]): + if scores[i] < request.input_data.min_confidence: + continue + + box = tuple(boxes[i].tolist()) + + # Boxes come in with normalized coordinates. Convert to pixel values. + box = [box[0] * image_width, box[1] * image_height, box[2] * image_width, box[3] * image_height] + + score = scores[i] + + if classes[i] in model.category_index.keys(): + label = model.category_index[classes[i]]['name'] + else: + label = 'N/A' + + num_objects += 1 + + print('Found object with label: "' + label + '" and score: ' + str(score)) + + + point1 = np.array([box[1], box[0]]) + point2 = np.array([box[3], box[0]]) + point3 = np.array([box[3], box[2]]) + point4 = np.array([box[1], box[2]]) + + # Add data to the output proto. + out_obj = out_proto.object_in_image.add() + out_obj.name = "obj" + str(num_objects) + "_label_" + label + + vertex1 = out_obj.image_properties.coordinates.vertexes.add() + vertex1.x = point1[0] + vertex1.y = point1[1] + + vertex2 = out_obj.image_properties.coordinates.vertexes.add() + vertex2.x = point2[0] + vertex2.y = point2[1] + + vertex3 = out_obj.image_properties.coordinates.vertexes.add() + vertex3.x = point3[0] + vertex3.y = point3[1] + + vertex4 = out_obj.image_properties.coordinates.vertexes.add() + vertex4.x = point4[0] + vertex4.y = point4[1] + + # Pack the confidence value. + confidence = wrappers_pb2.FloatValue(value=score) + out_obj.additional_properties.Pack(confidence) + + if not args.no_debug: + polygon = np.array([point1, point2, point3, point4], np.int32) + polygon = polygon.reshape((-1, 1, 2)) + cv2.polylines(image, [polygon], True, (0, 255, 0), 2) + + caption = "{}: {:.3f}".format(label, score) + left_x = min(point1[0], min(point2[0], min(point3[0], point4[0]))) + top_y = min(point1[1], min(point2[1], min(point3[1], point4[1]))) + cv2.putText(image, caption, (int(left_x), int(top_y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, + (0, 255, 0), 2) + + print('Found ' + str(num_objects) + ' object(s)') + + if not args.no_debug: + debug_image_filename = 'network_compute_server_output.jpg' + cv2.imwrite(debug_image_filename, image) + print('Wrote debug image output to: "' + debug_image_filename + '"') + + response_queue.put(out_proto) + + +class NetworkComputeBridgeWorkerServicer( + network_compute_bridge_service_pb2_grpc.NetworkComputeBridgeWorkerServicer): + + def __init__(self, thread_input_queue, thread_output_queue): + super(NetworkComputeBridgeWorkerServicer, self).__init__() + + self.thread_input_queue = thread_input_queue + self.thread_output_queue = thread_output_queue + + def NetworkCompute(self, request, context): + print('Got NetworkCompute request') + self.thread_input_queue.put(request) + out_proto = self.thread_output_queue.get() + return out_proto + + def ListAvailableModels(self, request, context): + print('Got ListAvailableModels request') + self.thread_input_queue.put(request) + out_proto = self.thread_output_queue.get() + return out_proto + + +def register_with_robot(options): + """ Registers this worker with the robot's Directory.""" + ip = bosdyn.client.common.get_self_ip(options.hostname) + print('Detected IP address as: ' + ip) + + sdk = bosdyn.client.create_standard_sdk("tensorflow_server") + + robot = sdk.create_robot(options.hostname) + + # Authenticate robot before being able to use it + if options.payload_credentials_file: + robot.authenticate_from_payload_credentials( + *bosdyn.client.util.get_guid_and_secret(options)) + else: + bosdyn.client.util.authenticate(robot) + + directory_client = robot.ensure_client( + bosdyn.client.directory.DirectoryClient.default_service_name) + directory_registration_client = robot.ensure_client( + bosdyn.client.directory_registration.DirectoryRegistrationClient.default_service_name) + + # Check to see if a service is already registered with our name + services = directory_client.list() + for s in services: + if s.name == options.name: + print("WARNING: existing service with name, \"" + options.name + "\", removing it.") + directory_registration_client.unregister(options.name) + break + + # Register service + print('Attempting to register ' + ip + ':' + options.port + ' onto ' + options.hostname + ' directory...') + directory_registration_client.register(options.name, "bosdyn.api.NetworkComputeBridgeWorker", kServiceAuthority, ip, int(options.port)) + + + +def main(argv): + default_port = '50051' + + parser = argparse.ArgumentParser() + parser.add_argument('-m', '--model', help='[MODEL_DIR] [LABELS_FILE.pbtxt]: Path to a model\'s directory and path to its labels .pbtxt file', action='append', nargs=2, required=True) + parser.add_argument('-p', '--port', help='Server\'s port number, default: ' + default_port, + default=default_port) + parser.add_argument('-d', '--no-debug', help='Disable writing debug images.', action='store_true') + parser.add_argument('-n', '--name', help='Service name', default='object-detection-coco') + bosdyn.client.util.add_payload_credentials_arguments(parser, required=False) + bosdyn.client.util.add_base_arguments(parser) + + options = parser.parse_args(argv) + + print(options.model) + + for model in options.model: + if not os.path.isdir(model[0]): + print('Error: model directory (' + model[0] + ') not found or is not a directory.') + sys.exit(1) + + # Perform registration. + register_with_robot(options) + + # Thread-safe queues for communication between the GRPC endpoint and the ML thread. + request_queue = queue.Queue() + response_queue = queue.Queue() + + # Start server thread + thread = threading.Thread(target=process_thread, args=([options, request_queue, response_queue])) + thread.start() + + # Set up GRPC endpoint + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + network_compute_bridge_service_pb2_grpc.add_NetworkComputeBridgeWorkerServicer_to_server( + NetworkComputeBridgeWorkerServicer(request_queue, response_queue), server) + server.add_insecure_port('[::]:' + options.port) + server.start() + + print('Running...') + thread.join() + + return True + +if __name__ == '__main__': + logging.basicConfig() + if not main(sys.argv[1:]): + sys.exit(1) diff --git a/jsk_spot_robot/coreio_extension/Dockerfile b/jsk_spot_robot/coreio_extension/Dockerfile new file mode 100644 index 0000000000..bb60defe90 --- /dev/null +++ b/jsk_spot_robot/coreio_extension/Dockerfile @@ -0,0 +1,42 @@ +# Use a base image provided by nvidia that already contains tensorflow 2.5 +## FROM nvcr.io/nvidia/l4t-tensorflow:r32.6.1-tf2.5-py3 +## https://docs.nvidia.com/deeplearning/frameworks/support-matrix/index.html#framework-matrix-2019 +FROM nvcr.io/nvidia/tensorflow:19.10-py3 + + +# Do some basic apt and pip updating +RUN apt-get update && \ + apt-get install -y --no-install-recommends python3-pip libgl1 &&\ + apt-get clean + +# Copy over the python requirements file and our prebuilt models API library +COPY docker-requirements.txt prebuilt/*.whl ./ +COPY models-with-protos models-with-protos + + +# Install the python requirements +RUN python3 -m pip install pip==21.3.1 setuptools==59.6.0 wheel==0.37.1 && \ + python3 -m pip install -r docker-requirements.txt --find-links . + +# https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch2 +RUN python3 -m pip install opencv-python==4.5.* +RUN python3 -m pip install --upgrade pip && \ + python3 -m pip install tensorflow-gpu==2.3.1 tensorflow==2.3.1 tensorboard==2.3.0 tf-models-official==2.3.0 pycocotools lvis && \ + python3 -m pip uninstall -y opencv-python-headless + + +RUN python3 -m pip install models-with-protos/research + +# copy pre-trained model +COPY ssd_resnet50_v1_fpn_640x640_coco17_tpu-8 /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8 +RUN cp models-with-protos/research/object_detection/data/mscoco_label_map.pbtxt /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/ + +# copy trained model +COPY dogtoy-model /data/dogtoy-model + +# Copy over our main script +COPY network_compute_server.py /app/ +WORKDIR /app + +# Set our script as the main entrypoint for the container +ENTRYPOINT ["python3", "network_compute_server.py"] diff --git a/jsk_spot_robot/coreio_extension/Makefile b/jsk_spot_robot/coreio_extension/Makefile new file mode 100644 index 0000000000..ab1675d527 --- /dev/null +++ b/jsk_spot_robot/coreio_extension/Makefile @@ -0,0 +1,29 @@ +HOST_IP=192.168.50.3 # Or 10.0.0.3 + +all: + [ -e models-with-protos.zip ] || wget https://dev.bostondynamics.com/docs/python/fetch_tutorial/files/models-with-protos.zip + [ -e models-with-protos ] || unzip models-with-protos.zip + [ -e ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.tar.gz ] || wget http://download.tensorflow.org/models/object_detection/tf2/20200711/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.tar.gz + [ -e ssd_resnet50_v1_fpn_640x640_coco17_tpu-8 ] || tar -xvzf ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.tar.gz + [ -e dogtoy-model ] || cp -r ../dogtoy/exported-models/dogtoy-model . + [ -e dogtoy-model/label_map.pbtxt ] || cp ../dogtoy/annotations/label_map.pbtxt dogtoy-model/ + docker build -t object_detection_coco -f Dockerfile . + +run: + docker run --network host \ + -v /opt/payload_credentials/payload_guid_and_secret:/opt/payload_credentials/payload_guid_and_secret \ + --rm object_detection_coco \ + -m /data/dogtoy-model/saved_model /data/dogtoy-model/label_map.pbtxt \ + -m /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/saved_model \ + /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/mscoco_label_map.pbtxt \ + --payload-credentials-file /opt/payload_credentials/payload_guid_and_secret \ + --name ssd-resnet-coco-server \ + $(HOST_IP) + +export: + [ -e object_detection_coco.tar.gz ] || docker save object_detection_coco | pigz > object_detection_coco.tar.gz + # scp -P 20022 *.tgz to spot@10.0.0.3:/opt + # scp -P 20022 *.service spot@10.0.0.3:/lib/systemd/system/ + +bash: + docker run --gpus all --rm -it --entrypoint /bin/bash object_detection_coco diff --git a/jsk_spot_robot/coreio_extension/README.md b/jsk_spot_robot/coreio_extension/README.md new file mode 100644 index 0000000000..aae571ffdc --- /dev/null +++ b/jsk_spot_robot/coreio_extension/README.md @@ -0,0 +1,23 @@ +coreio extensions +----------------- + +Please follow https://dev.bostondynamics.com/docs/python/fetch_tutorial/fetch1 to get images, annotate target, training network. After that, we expect following directory structure. + +``` +- dogtoy/ + |- annotations/ + |- label_map.pbtxt + |- exported-models/ + |- dogtoy-model/ + |- checkpoint/ + |- pipeline.config + |- saved_model/ + |- models/ + |- pre-trained-models/ +``` + + +Then copy `dogtoy-model` directory within `dogtoy/exported-models/` to this directory and put `annotations/label_map.pbtxt` under `dogtoy-model`. See `Makefile` for detail. + +To build docker run `make all`. To export docker image, run `make export`. After that copy `object_detection_coco.tar.gz` to `/opt/` directory of SpotCORE. To copy the data, we recommend to connect the ethernet cable to the robot or dock and use `scp -P 20022 object_detection_coco.tar.gz spot@10.0.0.3:/tmp/` to copy data and then, login to to spotcore and run `sudo cp /tmp/object_detection_coco.tar.gz /opt`. Please keep backup before you overwrite `tar.gz` file. + diff --git a/jsk_spot_robot/coreio_extension/docker-compose.yml b/jsk_spot_robot/coreio_extension/docker-compose.yml new file mode 100644 index 0000000000..3d194245f2 --- /dev/null +++ b/jsk_spot_robot/coreio_extension/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.5" +services: + object_detection_coco: + image: object_detection_coco + network_mode: host + restart: unless-stopped + environment: + # This package couldn't be installed, but putting it on the path allows required access to the protos. + - PYTHONPATH=/models-with-protos/research/ + volumes: + # Mount payload credentials. + - /opt/payload_credentials:/opt/payload_credentials + # and ML models + - /data/.extensions/object_detection_coco/data:/data + # The command below is partial because the docker image is already configured with an entrypoint. + command: > + -m /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/saved_model /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/mscoco_label_map.pbtxt + --payload-credentials-file /opt/payload_credentials/payload_guid_and_secret + 10.0.0.3 +# 192.168.50.3 diff --git a/jsk_spot_robot/coreio_extension/docker-requirements.txt b/jsk_spot_robot/coreio_extension/docker-requirements.txt new file mode 100644 index 0000000000..12c96e14fe --- /dev/null +++ b/jsk_spot_robot/coreio_extension/docker-requirements.txt @@ -0,0 +1,40 @@ +bosdyn-api==3.2.0 + # via + # bosdyn-client + # bosdyn-core +bosdyn-client==3.2.0 + # via -r requirements.txt +bosdyn-core==3.2.0 + # via bosdyn-client +certifi==2022.5.18.1 + # via requests +charset-normalizer==2.0.12 + # via requests +deprecated==1.2.13 + # via + # bosdyn-client + # bosdyn-core +grpcio + #==1.46.3 (conflicts with base image) + # via bosdyn-client +idna==3.3 + # via requests +numpy==1.19.4 + # via bosdyn-client +protobuf==3.19.4 + # via bosdyn-api +pyjwt==2.4.0 + # via bosdyn-client +requests==2.27.1 + # via bosdyn-client +six + #==1.16.0 (conflicts with base image) + # via grpcio +urllib3==1.26.9 + # via requests +wrapt + #==1.14.1 (conflicts with base image) + # via deprecated +Pillow==8.4.0 +opencv-python==4.6.0.66 + # via network_compute_server.py \ No newline at end of file diff --git a/jsk_spot_robot/coreio_extension/ncb_object_detection_coco.service b/jsk_spot_robot/coreio_extension/ncb_object_detection_coco.service new file mode 100644 index 0000000000..40f869e6d8 --- /dev/null +++ b/jsk_spot_robot/coreio_extension/ncb_object_detection_coco.service @@ -0,0 +1,18 @@ +[Unit] +Description=Coco object detection container +After=docker.service +Wants=network-online.target docker.socket +Requires=docker.socket + +[Service] +Restart=always +User=root +ExecStartPre=/bin/bash -c "/usr/bin/docker container inspect object_detection_coco 2> /dev/null || /usr/bin/docker load < /opt/object_detection_coco.tar.gz;" +ExecStartPre=-/usr/bin/docker stop object_detection_coco +ExecStartPre=-/usr/bin/docker rm object_detection_coco +ExecStart=/usr/bin/docker run --name object_detection_coco --network host --rm -v /opt/payload_credentials/payload_guid_and_secret:/opt/payload_credentials/payload_guid_and_secret object_detection_coco -m /data/dogtoy-model/saved_model /data/dogtoy-model/label_map.pbtxt -m /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/saved_model /data/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/mscoco_label_map.pbtxt --payload-credentials-file /opt/payload_credentials/payload_guid_and_secret --name ssd-resnet-coco-server --port 50055 192.168.50.3 +ExecStop=/usr/bin/docker stop -t 10 object_detection_coco +TimeoutSec=900 + +[Install] +WantedBy=multi-user.target diff --git a/jsk_spot_robot/coreio_extension/network_compute_server.py b/jsk_spot_robot/coreio_extension/network_compute_server.py new file mode 100644 index 0000000000..9b697b8383 --- /dev/null +++ b/jsk_spot_robot/coreio_extension/network_compute_server.py @@ -0,0 +1,312 @@ +# Copyright (c) 2022 Boston Dynamics, Inc. All rights reserved. +# +# Downloading, reproducing, distributing or otherwise using the SDK Software +# is subject to the terms and conditions of the Boston Dynamics Software +# Development Kit License (20191101-BDSDK-SL). + +import argparse +import io +import os +import sys +import time +import logging + +import cv2 +from PIL import Image +import numpy as np + +from bosdyn.api import network_compute_bridge_service_pb2_grpc +from bosdyn.api import network_compute_bridge_pb2 +from bosdyn.api import image_pb2 +from bosdyn.api import header_pb2 +import bosdyn.client +import bosdyn.client.util +import grpc +from concurrent import futures +import tensorflow as tf + +import queue +import threading +from google.protobuf import wrappers_pb2 +from object_detection.utils import label_map_util + +kServiceAuthority = "fetch-tutorial-worker.spot.robot" + + +class TensorFlowObjectDetectionModel: + def __init__(self, model_path, label_path): + self.detect_fn = tf.saved_model.load(model_path) + self.category_index = label_map_util.create_category_index_from_labelmap(label_path, use_display_name=True) + self.name = os.path.basename(os.path.dirname(model_path)) + + def predict(self, image): + input_tensor = tf.convert_to_tensor(image) + input_tensor = input_tensor[tf.newaxis, ...] + detections = self.detect_fn(input_tensor) + + return detections + +def process_thread(args, request_queue, response_queue): + # Load the model(s) + models = {} + for model in args.model: + this_model = TensorFlowObjectDetectionModel(model[0], model[1]) + models[this_model.name] = this_model + + print('') + print('Service ' + args.name + ' running on port: ' + str(args.port)) + + print('Loaded models:') + for model_name in models: + print(' ' + model_name) + + while True: + request = request_queue.get() + + if isinstance(request, network_compute_bridge_pb2.ListAvailableModelsRequest): + out_proto = network_compute_bridge_pb2.ListAvailableModelsResponse() + for model_name in models: + out_proto.available_models.append(model_name) + if models[model_name].category_index is not None: + labels_msg = out_proto.labels.add() + labels_msg.model_name = model_name + for n in models[model_name].category_index.keys(): + labels_msg.available_labels.append(models[model_name].category_index[n]['name']) + response_queue.put(out_proto) + continue + else: + out_proto = network_compute_bridge_pb2.NetworkComputeResponse() + + # Find the model + if request.input_data.model_name not in models: + err_str = 'Cannot find model "' + request.input_data.model_name + '" in loaded models.' + print(err_str) + + # Set the error in the header. + out_proto.header.error.code = header_pb2.CommonError.CODE_INVALID_REQUEST + out_proto.header.error.message = err_str + response_queue.put(out_proto) + continue + + model = models[request.input_data.model_name] + + # Unpack the incoming image. + if request.input_data.image.format == image_pb2.Image.FORMAT_RAW: + pil_image = Image.open(io.BytesIO(request.input_data.image.data)) + if request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U8: + # If the input image is grayscale, convert it to RGB. + image = cv2.cvtColor(pil_image, cv2.COLOR_GRAY2RGB) + + elif request.input_data.image.pixel_format == image_pb2.Image.PIXEL_FORMAT_RGB_U8: + # Already an RGB image. + image = pil_image + + else: + print('Error: image input in unsupported pixel format: ', request.input_data.image.pixel_format) + response_queue.put(out_proto) + continue + + elif request.input_data.image.format == image_pb2.Image.FORMAT_JPEG: + dtype = np.uint8 + jpg = np.frombuffer(request.input_data.image.data, dtype=dtype) + image = cv2.imdecode(jpg, -1) + + if len(image.shape) < 3: + # If the input image is grayscale, convert it to RGB. + image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) + + image_width = image.shape[0] + image_height = image.shape[1] + + detections = model.predict(image) + + num_objects = 0 + + # All outputs are batches of tensors. + # Convert to numpy arrays, and take index [0] to remove the batch dimension. + # We're only interested in the first num_detections. + num_detections = int(detections.pop('num_detections')) + detections = {key: value[0, :num_detections].numpy() + for key, value in detections.items()} + + boxes = detections['detection_boxes'] + classes = detections['detection_classes'] + scores = detections['detection_scores'] + + + for i in range(boxes.shape[0]): + if scores[i] < request.input_data.min_confidence: + continue + + box = tuple(boxes[i].tolist()) + + # Boxes come in with normalized coordinates. Convert to pixel values. + box = [box[0] * image_width, box[1] * image_height, box[2] * image_width, box[3] * image_height] + + score = scores[i] + + if classes[i] in model.category_index.keys(): + label = model.category_index[classes[i]]['name'] + else: + label = 'N/A' + + num_objects += 1 + + print('Found object with label: "' + label + '" and score: ' + str(score)) + + + point1 = np.array([box[1], box[0]]) + point2 = np.array([box[3], box[0]]) + point3 = np.array([box[3], box[2]]) + point4 = np.array([box[1], box[2]]) + + # Add data to the output proto. + out_obj = out_proto.object_in_image.add() + out_obj.name = "obj" + str(num_objects) + "_label_" + label + + vertex1 = out_obj.image_properties.coordinates.vertexes.add() + vertex1.x = point1[0] + vertex1.y = point1[1] + + vertex2 = out_obj.image_properties.coordinates.vertexes.add() + vertex2.x = point2[0] + vertex2.y = point2[1] + + vertex3 = out_obj.image_properties.coordinates.vertexes.add() + vertex3.x = point3[0] + vertex3.y = point3[1] + + vertex4 = out_obj.image_properties.coordinates.vertexes.add() + vertex4.x = point4[0] + vertex4.y = point4[1] + + # Pack the confidence value. + confidence = wrappers_pb2.FloatValue(value=score) + out_obj.additional_properties.Pack(confidence) + + if not args.no_debug: + polygon = np.array([point1, point2, point3, point4], np.int32) + polygon = polygon.reshape((-1, 1, 2)) + cv2.polylines(image, [polygon], True, (0, 255, 0), 2) + + caption = "{}: {:.3f}".format(label, score) + left_x = min(point1[0], min(point2[0], min(point3[0], point4[0]))) + top_y = min(point1[1], min(point2[1], min(point3[1], point4[1]))) + cv2.putText(image, caption, (int(left_x), int(top_y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, + (0, 255, 0), 2) + + print('Found ' + str(num_objects) + ' object(s)') + + if not args.no_debug: + debug_image_filename = 'network_compute_server_output.jpg' + cv2.imwrite(debug_image_filename, image) + print('Wrote debug image output to: "' + debug_image_filename + '"') + + response_queue.put(out_proto) + + +class NetworkComputeBridgeWorkerServicer( + network_compute_bridge_service_pb2_grpc.NetworkComputeBridgeWorkerServicer): + + def __init__(self, thread_input_queue, thread_output_queue): + super(NetworkComputeBridgeWorkerServicer, self).__init__() + + self.thread_input_queue = thread_input_queue + self.thread_output_queue = thread_output_queue + + def NetworkCompute(self, request, context): + print('Got NetworkCompute request') + self.thread_input_queue.put(request) + out_proto = self.thread_output_queue.get() + return out_proto + + def ListAvailableModels(self, request, context): + print('Got ListAvailableModels request') + self.thread_input_queue.put(request) + out_proto = self.thread_output_queue.get() + return out_proto + + +def register_with_robot(options): + """ Registers this worker with the robot's Directory.""" + ip = bosdyn.client.common.get_self_ip(options.hostname) + print('Detected IP address as: ' + ip) + + sdk = bosdyn.client.create_standard_sdk("tensorflow_server") + + robot = sdk.create_robot(options.hostname) + + # Authenticate robot before being able to use it + if options.payload_credentials_file: + robot.authenticate_from_payload_credentials( + *bosdyn.client.util.get_guid_and_secret(options)) + else: + bosdyn.client.util.authenticate(robot) + + directory_client = robot.ensure_client( + bosdyn.client.directory.DirectoryClient.default_service_name) + directory_registration_client = robot.ensure_client( + bosdyn.client.directory_registration.DirectoryRegistrationClient.default_service_name) + + # Check to see if a service is already registered with our name + services = directory_client.list() + for s in services: + if s.name == options.name: + print("WARNING: existing service with name, \"" + options.name + "\", removing it.") + directory_registration_client.unregister(options.name) + break + + # Register service + print('Attempting to register ' + ip + ':' + options.port + ' onto ' + options.hostname + ' directory...') + directory_registration_client.register(options.name, "bosdyn.api.NetworkComputeBridgeWorker", kServiceAuthority, ip, int(options.port)) + + + +def main(argv): + default_port = '50051' + + parser = argparse.ArgumentParser() + parser.add_argument('-m', '--model', help='[MODEL_DIR] [LABELS_FILE.pbtxt]: Path to a model\'s directory and path to its labels .pbtxt file', action='append', nargs=2, required=True) + parser.add_argument('-p', '--port', help='Server\'s port number, default: ' + default_port, + default=default_port) + parser.add_argument('-d', '--no-debug', help='Disable writing debug images.', action='store_true') + parser.add_argument('-n', '--name', help='Service name', default='fetch-server') + bosdyn.client.util.add_payload_credentials_arguments(parser, required=False) + bosdyn.client.util.add_base_arguments(parser) + + options = parser.parse_args(argv) + + print(options.model) + + for model in options.model: + if not os.path.isdir(model[0]): + print('Error: model directory (' + model[0] + ') not found or is not a directory.') + sys.exit(1) + + # Perform registration. + register_with_robot(options) + + # Thread-safe queues for communication between the GRPC endpoint and the ML thread. + request_queue = queue.Queue() + response_queue = queue.Queue() + + # Start server thread + thread = threading.Thread(target=process_thread, args=([options, request_queue, response_queue])) + thread.start() + + # Set up GRPC endpoint + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + network_compute_bridge_service_pb2_grpc.add_NetworkComputeBridgeWorkerServicer_to_server( + NetworkComputeBridgeWorkerServicer(request_queue, response_queue), server) + server.add_insecure_port('[::]:' + options.port) + server.start() + + print('Running...') + thread.join() + + return True + +if __name__ == '__main__': + logging.basicConfig() + if not main(sys.argv[1:]): + sys.exit(1) diff --git a/jsk_spot_robot/jsk_spot_driver.rosinstall b/jsk_spot_robot/jsk_spot_driver.rosinstall new file mode 100644 index 0000000000..021b6953c2 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_driver.rosinstall @@ -0,0 +1,89 @@ +# spot-ros is required for spot +# This is a develop branch for jsk version. +# We need to use it until it is merged to master +# - git: +# local-name: spot-ros +# uri: https://github.com/sktometometo/spot_ros.git +# version: develop/spot +## use https://github.com/clearpathrobotics/spot_ros/pull/67 (Commands for the Arm and gripper, and URDF and simulation of the arm added) +# - git: +# local-name: spot-ros +# uri: https://github.com/estherRay/spot_ros.git +# version: b5fe5e5c4a732c1a724e4f86abc2be6395088d02 +## use https://github.com/cst0/spot_ros/pull/4 for Arm and gripper +- git: + local-name: spot-ros + uri: https://github.com/k-okada/spot_ros-arm.git + version: 0a8c4b29768ae946d7c84aec9a2ae2d185ed97a9 +## to support custom limb name, we need https://github.com/jsk-ros-pkg/jsk_model_tools/pull/249 +- git: + local-name: jsk_model_tools + uri: https://github.com/k-okada/jsk_model_tools.git + version: custom_limb +# +# geometry2 have to be built for python3 +- git: + local-name: geometry2 + uri: https://github.com/ros/geometry2.git + version: 0.6.5 +# vision_opencv have to be build for python3 +- git: + local-name: vision_opencv + uri: https://github.com/ros-perception/vision_opencv.git + version: 1.13.0 +# +# pykdl have to be built for python3 +- git: + local-name: orocos_kinematics_dynamics + uri: https://github.com/orocos/orocos_kinematics_dynamics.git + version: v1.4.0 +# +# mongodb_store need to be built for python3 +- git: + local-name: mongodb_store + uri: https://github.com/strands-project/mongodb_store.git + version: noetic-devel +# +# to avtivate python_catkin_install, we need newer catkin +- git: + local-name: catkin + uri: https://github.com/ros/catkin.git + version: 0.8.10 +# +# use robot_upstart to install supervisor (https://github.com/clearpathrobotics/robot_upstart/pull/113) +- git: + local-name: robot_upstart + uri: https://github.com/k-okada/robot_upstart.git + version: develop +### aques_talk does not have arm64 libraries +# # aques_talk needs to be compiled from source +# - tar: +# local-name: aques_talk +# uri: https://github.com/tork-a/jsk_3rdparty-release/archive/refs/tags/release/melodic/aques_talk/2.1.24-2.tar.gz +# use parallel app_manager +- git: + local-name: app_manager + uri: https://github.com/PR2/app_manager.git + version: kinetic-devel +# wait for https://github.com/jsk-ros-pkg/jsk_pr2eus/pull/443 (add volume key arg for robot-interface :speak) to be released +- git: + local-name: jsk_pr2eus + uri: https://github.com/jsk-ros-pkg/jsk_pr2eus.git + version: master +# +# +# need to add following code to ublox_gps/config/zed_f9p.yaml +#config_on_startup: true +# +#rate: 0.016 +## TMODE3 Config +#tmode3: 1 # Survey-In Mode +#sv_in: +# reset: false # True: disables and re-enables survey-in (resets) +# # False: Disables survey-in only if TMODE3 is +# # disabled +# min_dur: 300 # Survey-In Minimum Duration [s] +# acc_lim: 3.0 # Survey-In Accuracy Limit [m] +- git: + local-name: ublox + uri: https://github.com/KumarRobotics/ublox.git diff --git a/jsk_spot_robot/jsk_spot_startup/CMakeLists.txt b/jsk_spot_robot/jsk_spot_startup/CMakeLists.txt new file mode 100644 index 0000000000..a9e4f14e83 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 2.8.3) +project(jsk_spot_startup) + +find_package(catkin REQUIRED) + + +################################### +## catkin specific configuration ## +################################### +catkin_package() + + +############# +## Install ## +############# +install(DIRECTORY auth config launch scripts services udev_rules template + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS +) + +file(GLOB_RECURSE SCRIPT_PROGRAMS node_scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/node_scripts) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/node_scripts) + endif() +endforeach() + + +############# +## Testing ## +############# +if(CATKIN_ENABLE_TESTING) + find_package(catkin REQUIRED COMPONENTS roslaunch roslint) + file(GLOB LAUNCH_FILES launch/*.launch) + foreach(LAUNCH_FILE ${LAUNCH_FILES}) + roslaunch_add_file_check(${LAUNCH_FILE}) + endforeach() + + set(ROSLINT_PYTHON_OPTS --max-line-length=180 --ignore=E221,E222,E241) # skip multiple spaces before/after operator + roslint_python() + roslint_add_test() +endif() + +############# +## Install ## +############# +file(GLOB_RECURSE SCRIPT_PROGRAMS node_scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/node_scripts) + endif() +endforeach() + +install(DIRECTORY app config launch + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS) + +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} nao.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + diff --git a/jsk_spot_robot/jsk_spot_startup/apps/app.installed b/jsk_spot_robot/jsk_spot_startup/apps/app.installed new file mode 100644 index 0000000000..2bf50ef955 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/app.installed @@ -0,0 +1,6 @@ +apps: +- app: jsk_spot_startup/hello_world + dispay: Hello World +- app: jsk_spot_startup/head_lead_demo + dispay: Head Lead Demo + \ No newline at end of file diff --git a/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head-lead-teleop.l b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head-lead-teleop.l new file mode 100755 index 0000000000..8548d7f372 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head-lead-teleop.l @@ -0,0 +1,297 @@ +#!/usr/bin/env roseus + +(ros::roseus-add-msgs "std_msgs") + +(setq *continue* t) +;; control head-lead +(setq *head-lead* t) +;; (setq *head-lead* nil) +(load "package://spoteus/spot-interface.l") +(spot-init) +(if (eq (send *ri* :state :power-state-shore-power-state) 'on-shore-power) + (send *ri* :undock)) +;; (send *ri* :set-impedance-param :linear-stiffness #f(250 250 250) :rotational-stiffness #f(30 30 30) :linear-damping #f(1.5 1.5 1.5) :rotational-damping #f(0.5 0.5 0.5)) +;; (send *ri* :set-impedance-param :linear-stiffness #f(250 25 25) :rotational-stiffness #f(30 5 5) :linear-damping #f(1.5 1.5 1.5) :rotational-damping #f(0.5 0.5 0.5)) + +(setq *default-av* #f(0.0 -130.0 120.0 0.0 10.0 0.0)) + +;; sample +(setq *person* nil) +(setq *ball* nil) +(setq *ball-name* nil) +(setq *last-detected-time* (ros::time-now)) +(defclass ncb-result-synchronizer + :super exact-time-message-filter) + +(defmethod ncb-result-synchronizer + (:callback (bbox rects result) + (setq *last-detected-time* (ros::time-now)) + (let (len ret) + (setq len (length (send bbox :boxes))) + (dotimes (i len) + (ros::ros-info (format nil "found ~A (~A) in ~A sec ago in ~A" + (elt (send result :label_names) i) + (elt (send result :label_proba) i) + (send (ros::time- (ros::time-now) (send bbox :header :stamp)) :to-sec) + (cdr (assoc "topic" (send result :connection-header) :test #'string=)))) + (cond ((string= (elt (send result :label_names) i) "person") + (send *ri* :speak "p") + (push (list (send result :header :frame_id) (elt (send rects :rects) i) (elt (send bbox :boxes) i)) *person*)) + ((or (string= (elt (send result :label_names) i) "soccer ball") + (string= (elt (send result :label_names) i) "pokemon ball") + (string= (elt (send result :label_names) i) "dice")) + (send *ri* :speak "ball") + (push (list (send result :header :frame_id) + (elt (send rects :rects) i) + (elt (send bbox :boxes) i) + (elt (send result :label_names) i)) + *ball*)))) + ;; results are (list image rect box); to get box user (elt x 2) or (third x) + (setq *person* (sort *person* #'< #'(lambda (x) (send (elt x 2) :value)))) + (setq *ball* (sort *ball* #'< #'(lambda (x) (send (elt x 2) :value)))) + )) ;; :callback + ) +(ros::roseus-add-msgs "jsk_recognition_msgs") + +(setq synchronizers nil) +(dolist (image (list "left_fisheye_image" "right_fisheye_image" + "frontleft_fisheye_image" "frontright_fisheye_image" + "hand_color_image" "back_fisheye_image")) + (push (instance ncb-result-synchronizer :init + (list (list (format nil "/spot/ncb_provider/~A/bbox_array" image) jsk_recognition_msgs::BoundingBoxArray) + (list (format nil "/spot/ncb_provider/~A/rects" image) jsk_recognition_msgs::RectArray) + (list (format nil "/spot/ncb_provider/~A/class" image) jsk_recognition_msgs::ClassificationResult))) synchronizers)) + +(defun init-walk-arm-setting () + (send *spot* :arm :angle-vector #f(0.0 -130.0 120.0 0.0 10.0 0.0)) + (send *ri* :angle-vector (send *spot* :angle-vector) 1000 :default-controller) + (send *ri* :stop-grasp) + (send *ri* :wait-interpolation) + (send *ri* :set-impedance-param + :linear-stiffness #f(250 25 75) ;; z(up) y(side) x(front) + :rotational-stiffness #f(3 30 30) + :linear-damping #f(1.5 1.0 1.0) + :rotational-damping #f(0.1 0.5 0.5)) + (setq hand-pos (ros::pos->tf-point (send *spot* :hand :end-coords :worldpos))) + (ros::set-param "end_effector_to_joy/center_x" (send hand-pos :x)) + (ros::set-param "end_effector_to_joy/center_y" (send hand-pos :y)) + (unless (ros::wait-for-service "end_effector_to_joy/set_enabled" 10) + (ros::ros-error "end_effector_to_joy/set_enabled not found, so quitting") + (return-from init-walk-arm-setting :finished)) + (ros::service-call "end_effector_to_joy/set_enabled" (instance std_srvs::SetBoolRequest :init :data *head-lead*)) + ) + +(defun start-func (args) + (when (not (eq (send *ri* :state :power-state-shore-power-state) 'off-shore-power)) + (send *ri* :speak "Robot is on dock") + (ros::ros-error "Robot is on dock or powered, quit from apps") + ;(return-from start-func :finished) + ) + (send *ri* :speak "Hello, let's start walking") + (if *head-lead* + (init-walk-arm-setting) + (progn + (send *spot* :arm :angle-vector #f(0 -180 180 90 0 -90)) + (send *ri* :angle-vector (send *spot* :angle-vector) 2000) + (send *ri* :stow-arm) + (send *ri* :stop-grasp) + (send *ri* :wait-interpolation))) + :started) + +(defun end-func (args) + (ros::ros-info "end-func") + (send *ri* :speak "Thank You") + (send *ri* :stow-arm) + (unless (ros::wait-for-service "end_effector_to_joy/set_enabled" 10) + (ros::ros-error "end_effector_to_joy/set_enabled not found, so quitting") + (return-from end-func :finished)) + (ros::service-call "end_effector_to_joy/set_enabled" (instance std_srvs::SetBoolRequest :init :data nil)) + :finished) + +;; *cmd-vel* is (cons (ros::time-now) msg) +(defun walk-func (args) + (let ((still-sec 0) + (countdown 0)) + (ros::ros-info "walk-func") + (do-until-key + (ros::rate 2) + (ros::spin-once) + (if *cmd-vel* + (setq still-sec (send (ros::time- (ros::time-now) (car *cmd-vel*)) :to-sec))) + (ros::ros-info "continue... ~A, still-sec ~A" *continue* still-sec) + (if (null *continue*) (return-from walk-func :finished)) + (if *cmd-vel* + (ros::ros-info "cmd-vel ... ~A sec ago ~A (~A)" + still-sec + (float-vector (send (cdr *cmd-vel*) :linear :x) (send (cdr *cmd-vel*) :linear :y) (send (cdr *cmd-vel*) :angular :z)) + (norm (float-vector (send (cdr *cmd-vel*) :linear :x) (send (cdr *cmd-vel*) :linear :y) (send (cdr *cmd-vel*) :angular :z))))) + (when (> still-sec countdown) + (send *ri* :speak (format nil "~A" (case countdown (0 "z") (2 "fu") (4 "shi") (6 "mu") (8 "ya") (10 "tou") (t countdown)))) + (setq countdown (+ (ceiling still-sec) 1))) + (if (and *cmd-vel* (> still-sec 10)) + (return-from walk-func :not-pull)) + (ros::sleep)) + :finished)) + +(setq *distance-thre* 2000) +(defun ball-play-func (args) + (ros::ros-info "ball-play") + (ros::spin-once) + (block + :cond + (cond ((send *ri* :interpolatingp) + (ros::ros-info "robot is moving...")) + (*ball* + (send *ri* :speak "found ball") + (ros::service-call "end_effector_to_joy/set_enabled" (instance std_srvs::SetBoolRequest :init :data nil)) + (ros::ros-info "found ball ~A" *ball*) + (ros::ros-info "found ~A" (mapcar #'ros::tf-pose->coords (send-all (mapcar #'third *ball*) :pose))) + (ros::ros-info " ~A" (mapcar #'second *ball*)) + (ros::ros-info " ~A" (mapcar #'fourth *ball*)) + (send *ri* :stow-arm) + (let ((image-source (first (car *ball*))) + (bbox (third (car *ball*))) + (rect (second (car *ball*))) + (ball-name (fourth (car *ball*))) + result) + (ros::ros-info "distance ~A (< ~A)" (norm (send (ros::tf-pose->coords (send bbox :pose)) :worldpos)) *distance-thre*) + (when (> (norm (send (ros::tf-pose->coords (send bbox :pose)) :worldpos)) *distance-thre*) + (send *ri* :speak "too far") + (setq *ball* nil) + (return-from :cond nil)) + ;; look at the ball + (send *spot* :head :look-at + (v+ (float-vector 0 0 (/ (send bbox :dimensions :z) 2)) + (send (ros::tf-pose->coords (send bbox :pose)) :worldpos))) + (send *ri* :angle-vector (send *spot* :angle-vector) 1000) + (send *ri* :wait-interpolation) + (unix::sleep 1) + (send *ri* :pick-object-in-image image-source + (+ (send rect :x) (/ (send rect :width) 2)) + (+ (send rect :y) (/ (send rect :height) 2))) + (ros::publish "/spot/pick_object_in_image/pick_object" + (instance std_msgs::String :init :data ball-name)) + (while (= (send *ri* :pick-object-in-image-get-state) actionlib_msgs::GoalStatus::*active*) + (ros::ros-info "wait for pick... ~A" + (if (send *ri* :pick-object-in-image-feedback-msg) + (send *ri* :pick-object-in-image-feedback-msg :feedback :status))) + (ros::duration-sleep 1)) + (setq result (send *ri* :pick-object-in-image-wait-for-result)) + (if (send result :success) + (progn + (send *ri* :speak "good") + (ros::ros-info "succeeded to pick object")) + (progn + (send *ri* :speak "too bad") + (ros::ros-info "failed to pick object") + (send *ri* :stop-grasp) + ;;(unix::sleep 2) ;; ?? prevent from walking???? + )) + (if *head-lead* + (progn + (send *spot* :arm :angle-vector *default-av*) + (send *ri* :angle-vector (send *spot* :angle-vector) 1000) + (send *ri* :wait-interpolation) + ) + (progn + (send *spot* :arm :angle-vector #f(0 -180 180 90 0 -90)) + (send *ri* :angle-vector (send *spot* :angle-vector) 2000) + (send *ri* :wait-interpolation) + (send *ri* :stow-arm) + )) + (when (send result :success) + (send *ri* :go-pos -0.5 0 0) + (when *head-lead* + (send *spot* :arm :wrist-p :joint-angle 30) + (send *ri* :angle-vector (send *spot* :angle-vector) 500)) + (send *ri* :stop-grasp)) + (setq *ball* nil) + (ros::rate 1) + (if *head-lead* (init-walk-arm-setting)) + )) + (*person* + (send *ri* :speak "found person") + (ros::ros-info "found person ~A" *person*) + (ros::ros-info "found person ~A" (mapcar #'ros::tf-pose->coords (send-all (mapcar #'third *person*) :pose))) + (ros::ros-info " ~A" (mapcar #'second *person*)) + (ros::service-call "end_effector_to_joy/set_enabled" (instance std_srvs::SetBoolRequest :init :data nil)) + (send *spot* :arm :angle-vector *default-av*) + (send *spot* :head :look-at (v+ + (float-vector 0 0 (/ (send (third (car *person*)) :dimensions :z) 2)) + (send (ros::tf-pose->coords (send (third (car *person*)) :pose)) :worldpos))) + (send *ri* :angle-vector (send *spot* :angle-vector) 500) + (setq *person* nil) + (send *ri* :wait-interpolation) + (if *head-lead* (init-walk-arm-setting))) + (t + (ros::ros-info (format nil "nothing found ..... last detected object is ~A sec ago" (send (ros::time- (ros::time-now) *last-detected-time*) :to-sec))) + (when (and + *head-lead* + (> (send (ros::time- (ros::time-now) *last-detected-time*) :to-sec) 10) + (> (norm (v- (coerce (mapcar #'(lambda (x) (elt (send *ri* :state :angle-vector) x)) (mapcar #'(lambda (x) (position x (send *spot* :joint-list))) (send *spot* :arm :joint-list))) float-vector) *default-av*)) 10)) + (ros::service-call "end_effector_to_joy/set_enabled" (instance std_srvs::SetBoolRequest :init :data nil)) + (send *spot* :arm :angle-vector *default-av*) + (send *ri* :angle-vector (send *spot* :angle-vector) 500) + (send *ri* :wait-interpolation) + (init-walk-arm-setting) + )) + )) ;; cond + :walk) +;; +(load "package://roseus_smach/src/state-machine-ros.l") +(defun walk-sm () + (let (sm) + (setq sm + (make-state-machine + '((:start :started :walk) ;; transitions (node transition node) + (:start :finished :end) + (:walk :finished :end) + (:walk :not-pull :ball-play) + (:ball-play :walk :walk) + (:ball-play :finished :end) + (:end :finished :goal) + ) + '((:start 'start-func) ;; node-to-function maps + (:end 'end-func) + (:walk 'walk-func) + (:ball-play 'ball-play-func) + ) + '(:start) ;; initial node + '(:goal) ;; goal node + )) + (send sm :arg-keys 'description) + sm)) + +;; create robot interface +(unless (boundp '*ri*) (spot-init)) +(objects (list *spot*)) + +;; + +;; this does not work... +;; (unix:signal unix::sigint '(lambda-closure nil 0 0 (sig code) (setq *continue* nil))) + +;; https://github.com/jsk-ros-pkg/jsk_roseus/pull/717 +;; did not work, when we subscribe image data ??? + +(defun ros::roseus-sigint-handler (sig code) + (ros::ros-warn (format nil "ros::roseus-sigint-handler ~A" sig)) + (setq *continue* nil)) +(unix:signal unix::sigint 'ros::roseus-sigint-handler) + +;; send start-func (set-impedance) when R Stick button is pressed +(ros::subscribe "/joy_dualsense" sensor_msgs::Joy + #'(lambda (msg) + (if (and (> (length (send msg :buttons)) 11) + (= (elt (send msg :buttons) 11) 1)) + (start-func nil))) + 20) +(setq *cmd-vel* (cons (ros::time-now) (instance geometry_msgs::Twist :init))) +(ros::subscribe "/spot/cmd_vel" geometry_msgs::Twist + #'(lambda (msg) (setq *cmd-vel* (cons (ros::time-now) msg))) 20) + +(ros::advertise "/spot/pick_object_in_image/pick_object" std_msgs::String 1 t) + +;; state machine +(exec-state-machine (walk-sm) '((description . "お散歩しました!")(image . ""))) +(exit) diff --git a/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.app b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.app new file mode 100644 index 0000000000..500d55e80a --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.app @@ -0,0 +1,5 @@ +display: Head Lead Demo +platform: spot +launch: jsk_spot_startup/head_lead_demo.xml +interface: jsk_spot_startup/head_lead_demo.interface + diff --git a/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.interface b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.interface new file mode 100644 index 0000000000..c27c9c296e --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.interface @@ -0,0 +1,3 @@ +published_topics: {} +subscribed_topics: {} + diff --git a/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.xml b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.xml new file mode 100644 index 0000000000..bee6b6b5e4 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/head_lead_demo/head_lead_demo.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello-world.l b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello-world.l new file mode 100755 index 0000000000..3f6ad67077 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello-world.l @@ -0,0 +1,78 @@ +#!/usr/bin/env roseus + +(setq *continue* t) +(load "package://spoteus/spot-interface.l") +(spot-init) +(if (eq (send *ri* :state :power-state-shore-power-state) 'on-shore-power) + (send *ri* :undock)) + +(defun init-pose () + (send *spot* :reset-pose) + (send *spot* :arm :move-end-pos #f(0 0 250) :world) + ) + +(defun start-func (args) + (init-pose) + (send *ri* :angle-vector (send *spot* :angle-vector) 500 :default-controller) + (send *ri* :wait-interpolation) + :started) + +(defun end-func (args) + (send *ri* :stow-arm) + :finished) + +(defun hello-func (args) + (send *ri* :speak "Hello" :volume 1.0) + (init-pose) + (send *spot* :arm :move-end-rot -20 :z :parent) + (send *spot* :arm :move-end-rot -10 :x :parent) + (send *ri* :angle-vector (send *spot* :angle-vector) 150 :default-controller) + (send *ri* :wait-interpolation) + ;; + (send *ri* :speak "World" :volume 1.0) + (init-pose) + (send *spot* :arm :move-end-rot 20 :z :parent) + (send *spot* :arm :move-end-rot -10 :x :parent) + (send *ri* :angle-vector (send *spot* :angle-vector) 150 :default-controller) + (send *ri* :wait-interpolation) + :finished) +;; +(load "package://roseus_smach/src/state-machine-ros.l") +(defun hello-sm () + (let (sm) + (setq sm + (make-state-machine + '((:start :started :hello) ;; transitions (node transition node) + (:hello :finished :end) + (:end :finished :goal) + ) + '((:start 'start-func) ;; node-to-function maps + (:end 'end-func) + (:hello 'hello-func) + ) + '(:start) ;; initial node + '(:goal) ;; goal node + )) + (send sm :arg-keys 'description) + sm)) + +;; create robot interface +(unless (boundp '*ri*) (spot-init)) +(objects (list *spot*)) + +;; + +;; this does not work... +;; (unix:signal unix::sigint '(lambda-closure nil 0 0 (sig code) (setq *continue* nil))) + +;; https://github.com/jsk-ros-pkg/jsk_roseus/pull/717 +;; did not work, when we subscribe image data ??? + +(defun ros::roseus-sigint-handler (sig code) + (ros::ros-warn (format nil "ros::roseus-sigint-handler ~A" sig)) + (setq *continue* nil)) +(unix:signal unix::sigint 'ros::roseus-sigint-handler) + +;; state machine +(exec-state-machine (hello-sm) '((description . "Hello World")(image . ""))) +(exit) diff --git a/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.app b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.app new file mode 100644 index 0000000000..ae8dc86a34 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.app @@ -0,0 +1,5 @@ +display: Hello World +platform: spot +launch: jsk_spot_startup/hello_world.xml +interface: jsk_spot_startup/hello_world.interface + diff --git a/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.interface b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.interface new file mode 100644 index 0000000000..c27c9c296e --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.interface @@ -0,0 +1,3 @@ +published_topics: {} +subscribed_topics: {} + diff --git a/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.xml b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.xml new file mode 100644 index 0000000000..15a5b233e7 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/apps/hello_world/hello_world.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/auth/spot_credential.yaml b/jsk_spot_robot/jsk_spot_startup/auth/spot_credential.yaml new file mode 100644 index 0000000000..a9e90c9bf1 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/auth/spot_credential.yaml @@ -0,0 +1,4 @@ +username: "dummyusername" +password: "dummypassword" +hostname: "192.168.50.3" +dock_id: 521 diff --git a/jsk_spot_robot/jsk_spot_startup/config/dualsense_teleop_twist_joy.yaml b/jsk_spot_robot/jsk_spot_startup/config/dualsense_teleop_twist_joy.yaml new file mode 100644 index 0000000000..bfd30e14b0 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/config/dualsense_teleop_twist_joy.yaml @@ -0,0 +1,17 @@ +axis_linear: + x: 1 + y: 0 +scale_linear: + x: 1.0 + y: 0.8 +scale_linear_turbo: + x: 1.5 + y: 1.2 +axis_angular: + yaw: 2 +scale_angular: + yaw: 1.0 +scale_angular_turbo: + yaw: 2.0 +enable_button: 4 +enable_turbo_button: 10 diff --git a/jsk_spot_robot/jsk_spot_startup/config/head_teleop_twist_joy.yaml b/jsk_spot_robot/jsk_spot_startup/config/head_teleop_twist_joy.yaml new file mode 100644 index 0000000000..364aaf2941 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/config/head_teleop_twist_joy.yaml @@ -0,0 +1,13 @@ +axis_linear: + x: 0 + y: 1 + x_min: -0.5 + x_max: 1.25 + y_min: -0.5 + y_max: 0.5 +axis_angular: + yaw: 2 + yaw_min: -0.75 + yaw_max: 0.75 +enable_button: 0 +enable_turbo_button: -1 diff --git a/jsk_spot_robot/jsk_spot_startup/config/spot.rviz b/jsk_spot_robot/jsk_spot_startup/config/spot.rviz new file mode 100644 index 0000000000..c732bf2766 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/config/spot.rviz @@ -0,0 +1,505 @@ +Panels: + - Class: rviz/Displays + Help Height: 0 + Name: Displays + Property Tree Widget: + Expanded: + - /DepthClouds1/FrontLeftDepthCloud1/Autocompute Value Bounds1 + - /DepthClouds1/FrontRightDepthCloud1/Autocompute Value Bounds1 + - /DepthClouds1/LeftDepthCloud1/Autocompute Value Bounds1 + - /DepthClouds1/RightDepthCloud1/Autocompute Value Bounds1 + - /DepthClouds1/RearDepthCloud1/Autocompute Value Bounds1 + Splitter Ratio: 0.6832844614982605 + Tree Height: 441 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Tool Properties + Expanded: + - /2D Pose Estimate1 + - /2D Nav Goal1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz/Views + Expanded: + - /Current View1 + - /Current View1/Focal Point1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Experimental: false + Name: Time + SyncMode: 0 + SyncSource: FrontLeftDepthCloud +Preferences: + PromptSaveOnExit: true +Toolbars: + toolButtonStyle: 2 +Visualization Manager: + Class: "" + Displays: + - Alpha: 1 + Class: rviz/RobotModel + Collision Enabled: false + Enabled: true + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Link Tree Style: Links in Alphabetic Order + base_link: + Alpha: 1 + Show Axes: false + Show Trail: false + body: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + front_left_hip: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + front_left_lower_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + front_left_upper_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + front_rail: + Alpha: 1 + Show Axes: false + Show Trail: false + front_right_hip: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + front_right_lower_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + front_right_upper_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + insta360_link: + Alpha: 1 + Show Axes: false + Show Trail: false + jsk_head_mount_frame: + Alpha: 1 + Show Axes: false + Show Trail: false + jsk_payload_frame: + Alpha: 1 + Show Axes: false + Show Trail: false + rear_left_hip: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + rear_left_lower_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + rear_left_upper_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + rear_rail: + Alpha: 1 + Show Axes: false + Show Trail: false + rear_right_hip: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + rear_right_lower_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + rear_right_upper_leg: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Name: RobotModel + Robot Description: robot_description + TF Prefix: "" + Update Interval: 0 + Value: true + Visual Enabled: true + - Class: rviz/TF + Enabled: false + Frame Timeout: 15 + Frames: + All Enabled: true + Marker Scale: 0.25 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: true + Tree: + {} + Update Interval: 0 + Value: false + - Class: rviz/Group + Displays: + - Alpha: 1 + Auto Size: + Auto Size Factor: 1 + Value: true + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 2.072983741760254 + Min Value: 0.4096323251724243 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/DepthCloud + Color: 255; 255; 255 + Color Image Topic: "" + Color Transformer: AxisColor + Color Transport Hint: raw + Decay Time: 0 + Depth Map Topic: /spot/depth/frontleft/image + Depth Map Transport Hint: raw + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: FrontLeftDepthCloud + Occlusion Compensation: + Occlusion Time-Out: 30 + Value: false + Position Transformer: XYZ + Queue Size: 5 + Selectable: true + Size (Pixels): 3 + Style: Flat Squares + Topic Filter: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Auto Size: + Auto Size Factor: 1 + Value: true + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 3.060708999633789 + Min Value: 0.31034958362579346 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/DepthCloud + Color: 255; 255; 255 + Color Image Topic: "" + Color Transformer: AxisColor + Color Transport Hint: raw + Decay Time: 0 + Depth Map Topic: /spot/depth/frontright/image + Depth Map Transport Hint: raw + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: FrontRightDepthCloud + Occlusion Compensation: + Occlusion Time-Out: 30 + Value: false + Position Transformer: XYZ + Queue Size: 5 + Selectable: true + Size (Pixels): 3 + Style: Flat Squares + Topic Filter: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Auto Size: + Auto Size Factor: 1 + Value: true + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 1.9569889307022095 + Min Value: 0.40128064155578613 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/DepthCloud + Color: 255; 255; 255 + Color Image Topic: "" + Color Transformer: AxisColor + Color Transport Hint: raw + Decay Time: 0 + Depth Map Topic: /spot/depth/left/image + Depth Map Transport Hint: raw + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: LeftDepthCloud + Occlusion Compensation: + Occlusion Time-Out: 30 + Value: false + Position Transformer: XYZ + Queue Size: 5 + Selectable: true + Size (Pixels): 3 + Style: Flat Squares + Topic Filter: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Auto Size: + Auto Size Factor: 1 + Value: true + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 2.684413433074951 + Min Value: 0.4553413391113281 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/DepthCloud + Color: 255; 255; 255 + Color Image Topic: "" + Color Transformer: AxisColor + Color Transport Hint: raw + Decay Time: 0 + Depth Map Topic: /spot/depth/right/image + Depth Map Transport Hint: raw + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: RightDepthCloud + Occlusion Compensation: + Occlusion Time-Out: 30 + Value: false + Position Transformer: XYZ + Queue Size: 5 + Selectable: true + Size (Pixels): 3 + Style: Flat Squares + Topic Filter: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Auto Size: + Auto Size Factor: 1 + Value: true + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 4.3172607421875 + Min Value: 0.1980423927307129 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/DepthCloud + Color: 255; 255; 255 + Color Image Topic: "" + Color Transformer: AxisColor + Color Transport Hint: raw + Decay Time: 0 + Depth Map Topic: /spot/depth/back/image + Depth Map Transport Hint: raw + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: RearDepthCloud + Occlusion Compensation: + Occlusion Time-Out: 30 + Value: false + Position Transformer: XYZ + Queue Size: 5 + Selectable: true + Size (Pixels): 3 + Style: Flat Squares + Topic Filter: true + Use Fixed Frame: true + Use rainbow: true + Value: true + Enabled: true + Name: DepthClouds + - Class: jsk_rviz_plugin/OverlayImage + Enabled: true + Name: PanoramaImage + Topic: /dual_fisheye_to_panorama/output + Value: true + alpha: 0.800000011920929 + height: 128 + keep aspect ratio: true + left: 0 + overwrite alpha value: false + top: 0 + transport hint: raw + width: 700 + - Class: jsk_rviz_plugin/OverlayImage + Enabled: true + Name: PanoramaDetection + Topic: /spot_recognition/object_detection_image + Value: true + alpha: 0.800000011920929 + height: 128 + keep aspect ratio: true + left: 0 + overwrite alpha value: false + top: 0 + transport hint: raw + width: 700 + - Class: jsk_rviz_plugin/TFTrajectory + Enabled: true + Name: BodyTrajectory + Value: true + color: 25; 255; 240 + duration: 1000 + frame: base_link + line_width: 0.009999999776482582 + - Class: jsk_rviz_plugin/PieChart + Enabled: true + Name: SpotBattery + Topic: /spot/status/battery_percentage + Value: true + auto color change: false + background color: 0; 0; 0 + backround alpha: 0 + clockwise rotate direction: false + foreground alpha: 0.699999988079071 + foreground alpha 2: 0.4000000059604645 + foreground color: 25; 255; 240 + left: 750 + max color: 255; 0; 0 + max value: 100 + min value: 0 + show caption: true + size: 128 + text size: 10 + top: 50 + - Class: jsk_rviz_plugin/PieChart + Enabled: true + Name: LaptopBattery + Topic: /spot/status/laptop_battery_percentage + Value: true + auto color change: false + background color: 0; 0; 0 + backround alpha: 0 + clockwise rotate direction: false + foreground alpha: 0.699999988079071 + foreground alpha 2: 0.4000000059604645 + foreground color: 25; 255; 240 + left: 900 + max color: 255; 0; 0 + max value: 100 + min value: 0 + show caption: true + size: 128 + text size: 10 + top: 50 + - Class: jsk_rviz_plugin/BoundingBoxArray + Enabled: true + Name: DetectedObjects + Topic: /spot_recognition/bbox_array + Unreliable: false + Value: true + alpha: 0.800000011920929 + color: 25; 255; 0 + coloring: Label + line width: 0.05000000074505806 + only edge: true + show coords: false + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Default Light: true + Fixed Frame: odom + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Theta std deviation: 0.2617993950843811 + Topic: /initialpose + X std deviation: 0.5 + Y std deviation: 0.5 + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Class: rviz/Orbit + Distance: 8.482810974121094 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Focal Point: + X: 0 + Y: 0 + Z: 0 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.3449980318546295 + Target Frame: base_link + Value: Orbit (rviz) + Yaw: 4.94684362411499 + Saved: ~ +Window Geometry: + Displays: + collapsed: true + Height: 1025 + Hide Left Dock: true + Hide Right Dock: true + QMainWindow State: 000000ff00000000fd00000004000000000000015700000363fc0200000012fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000003d00000363000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb000000280020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000000000000000fb0000001c004d006f00740069006f006e0050006c0061006e006e0069006e006700000002300000018e0000000000000000fb00000044004d006f00740069006f006e0050006c0061006e006e0069006e00670020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000000000000000fb0000000a0049006d00610067006501000001f2000001ae0000000000000000fb0000000a0049006d00610067006501000002f4000000ca0000000000000000fb0000000a0049006d006100670065000000013f000000660000000000000000fb0000000a0049006d0061006700650000000164000000940000000000000000fb0000000a0049006d00610067006500000001a3000000e80000000000000000fc00000227000001970000000000fffffffa000000000100000002fb0000000a0049006d0061006700650000000000ffffffff0000000000000000fb0000000a0049006d0061006700650000000000000002ad0000000000000000fb0000000a0049006d00610067006503000004f5000002820000028800000157000000010000010f00000363fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d00000363000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007800000003efc0100000002fb0000000800540069006d0065010000000000000780000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000007800000036300000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: true + Width: 1920 + X: 0 + Y: 27 diff --git a/jsk_spot_robot/jsk_spot_startup/config/twist_mux.yaml b/jsk_spot_robot/jsk_spot_startup/config/twist_mux.yaml new file mode 100644 index 0000000000..f5df2a4c8c --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/config/twist_mux.yaml @@ -0,0 +1,13 @@ +topics: +# +# priority 9: high <--> 1: low +# +- name : bt_joy + topic : bluetooth_teleop/cmd_vel + timeout : 0.5 + priority: 9 +- name : head_joy + topic : joy_head/cmd_vel + timeout : 0.5 + priority: 7 +locks: [] diff --git a/jsk_spot_robot/jsk_spot_startup/launch/include/driver.launch b/jsk_spot_robot/jsk_spot_startup/launch/include/driver.launch new file mode 100644 index 0000000000..6da02babdd --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/include/driver.launch @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/include/interaction.launch b/jsk_spot_robot/jsk_spot_startup/launch/include/interaction.launch new file mode 100644 index 0000000000..d4f6ae4788 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/include/interaction.launch @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/include/lifelog.launch b/jsk_spot_robot/jsk_spot_startup/launch/include/lifelog.launch new file mode 100644 index 0000000000..3e6195e067 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/include/lifelog.launch @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + approximate_sync: true + topics: + - /publish_trigger_mongodb_event + - /spot/camera/hand_color/image/compressed + - /spot/camera/frontleft/image/compressed + - /spot/camera/frontright/image/compressed + - /spot/camera/left/image/compressed + - /spot/camera/right/image/compressed + - /spot/camera/back/image/compressed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + topics: + - /spot/ncb_provider/back_fisheye_image/class + - /spot/ncb_provider/frontleft_fisheye_image/class + - /spot/ncb_provider/frontright_fisheye_image/class + - /spot/ncb_provider/hand_color_image/class + - /spot/ncb_provider/left_fisheye_image/class + - /spot/ncb_provider/right_fisheye_image/class + + + + + + + topics: + - /spot/ncb_provider/bbox_array + + + + + + topics: + - /ublox/fix + - /ublox/fix_velocity + + + + + periodic: false + threshold: 0.01 + + + + white_list: + type: + - control_msgs/FollowJointTrajectoryActionFeedback + - control_msgs/FollowJointTrajectoryActionGoal + - control_msgs/FollowJointTrajectoryActionResult + - pr2_common_action_msgs/TuckArmsActionFeedback + - pr2_common_action_msgs/TuckArmsActionGoal + - pr2_common_action_msgs/TuckArmsActionResult + - pr2_controllers_msgs/PointHeadActionFeedback + - pr2_controllers_msgs/PointHeadActionGoal + - pr2_controllers_msgs/PointHeadActionResult + - pr2_controllers_msgs/Pr2GripperCommandActionFeedback + - pr2_controllers_msgs/Pr2GripperCommandActionGoal + - pr2_controllers_msgs/Pr2GripperCommandActionResult + - sound_play/SoundRequestActionResult + - sound_play/SoundRequestActionGoal + - spot_msgs/PickObjectInImageActionGoal + - spot_msgs/PickObjectInImageActionResult + + + + topics: + - /sound_play/goal + - /robotsound/goal + - /robotsound_jp/goal + - /sound_play + - /robotsound + - /robotsound_jp + + + + topics: + - /faces_name + - /aws_auto_checkin_app/output/class + - /aws_detect_faces/attributes + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/include/lifelog_rgb_image.launch b/jsk_spot_robot/jsk_spot_startup/launch/include/lifelog_rgb_image.launch new file mode 100644 index 0000000000..6f0f1e4a28 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/include/lifelog_rgb_image.launch @@ -0,0 +1,19 @@ + + + + + + + + + + + + enable_monitor: false + vital_check: false + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/include/perception.launch b/jsk_spot_robot/jsk_spot_startup/launch/include/perception.launch new file mode 100644 index 0000000000..1772e298c1 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/include/perception.launch @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + image_sources: $(arg image_sources) + ml_service: $(arg ml_service) + ml_models: $(arg ml_models) + ml_confidence: $(arg ml_confidence) + use_gui: $(arg use_gui) + + + + + + + + + use_window: $(arg use_gui) + aws_credentials_path: $(arg aws_credential_config) + always_publish: false + attributes: ALL + + + + + + + + use_window: $(arg use_gui) + env_path: $(arg aws_credential_config) + aws_credentials_path: $(arg aws_credential_config) + always_publish: false + approximate_sync: true + queue_size: 1 + image_transport: compressed + + + + + + + + + font_path: $(find jsk_recognition_utils)/font_data/NotoSansJP-Regular.otf + use_classification_result: true + label_size: 16 + approximate_sync: true + queue_size: 30 + image_transport: compressed + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/include/peripheral.launch b/jsk_spot_robot/jsk_spot_startup/launch/include/peripheral.launch new file mode 100644 index 0000000000..6446752c6b --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/include/peripheral.launch @@ -0,0 +1,2 @@ + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/jsk_spot_bringup.launch b/jsk_spot_robot/jsk_spot_startup/launch/jsk_spot_bringup.launch new file mode 100644 index 0000000000..16234a445b --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/jsk_spot_bringup.launch @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/rosbag_play.launch b/jsk_spot_robot/jsk_spot_startup/launch/rosbag_play.launch new file mode 100644 index 0000000000..d3e8eee15a --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/rosbag_play.launch @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/rosbag_record.launch b/jsk_spot_robot/jsk_spot_startup/launch/rosbag_record.launch new file mode 100644 index 0000000000..b28009e057 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/rosbag_record.launch @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/rviz.launch b/jsk_spot_robot/jsk_spot_startup/launch/rviz.launch new file mode 100644 index 0000000000..2d9fce1f16 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/rviz.launch @@ -0,0 +1,11 @@ + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/sample_pick_object_in_image.launch b/jsk_spot_robot/jsk_spot_startup/launch/sample_pick_object_in_image.launch new file mode 100644 index 0000000000..103827e8e4 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/sample_pick_object_in_image.launch @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/launch/sample_walk_to_point_in_image.launch b/jsk_spot_robot/jsk_spot_startup/launch/sample_walk_to_point_in_image.launch new file mode 100644 index 0000000000..a22020a9c7 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/launch/sample_walk_to_point_in_image.launch @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/battery_notifier.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/battery_notifier.py new file mode 100755 index 0000000000..e35b6ceae2 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/battery_notifier.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +import rospy +from std_msgs.msg import Bool +from std_srvs.srv import Trigger, TriggerRequest +from spot_msgs.msg import BatteryStateArray +from sensor_msgs.msg import BatteryState + +from sound_play.libsoundplay import SoundClient + + +class SpotBatteryNotifier(object): + + def __init__(self): + + self._battery_spot = None + self._battery_temperature = 0 + self._is_connected = False + self.last_warn_bat_temp_time = rospy.get_time() + + self._sub_spot = rospy.Subscriber( + '/spot/status/battery_states', + BatteryStateArray, + self._cb_spot) + self._sub_connected = rospy.Subscriber( + '/spot/status/cable_connected', + Bool, + self._cb_connected) + + sound_client = SoundClient( + blocking=False, + sound_action='/robotsound_jp', + sound_topic='/robotsound_jp' + ) + + threshold_warning_battery_temperature =\ + float(rospy.get_param('~threshold_warning_battery_temperature', 45)) + + threshold_warning_spot = float( + rospy.get_param('~threshold_warning_spot', 20)) + + threshold_return_spot = float( + rospy.get_param('~threshold_return_spot', 15)) + + threshold_estop_spot = float( + rospy.get_param('~threshold_estop_spot', 5)) + + notification_duration = float( + rospy.get_param('~notification_duration', 300)) + + rate = rospy.Rate(0.1) + last_notified = rospy.Time.now() + while not rospy.is_shutdown(): + + rate.sleep() + + if not self._is_connected: + + if (rospy.Time.now() - last_notified).to_sec() > notification_duration: + sound_client.say('バッテリー残量、{}、パーセントです。'.format( + int(self._battery_spot)), volume=0.3) + last_notified = rospy.Time.now() + + if (self._battery_spot is not None and self._battery_spot < threshold_estop_spot): + rospy.logerr('Battery is low. Estop.') + sound_client.say('バッテリー残量が少ないため、動作を停止します') + rospy.ServiceProxy('/spot/estop/gentle', Trigger)(TriggerRequest()) + rospy.ServiceProxy('/spot/estop/hard', Trigger)(TriggerRequest()) + + elif (self._battery_spot is not None and self._battery_spot < threshold_return_spot): + rospy.logerr('Battery is low. Returning to home.') + sound_client.say('バッテリー残量が少ないため、ドックに戻ります') + self._call_go_back_home() + + elif (self._battery_spot is not None and self._battery_spot < threshold_warning_spot): + rospy.logwarn('Battery is low. Spot: {}'.format(self._battery_spot)) + sound_client.say('バッテリー残量が少ないです。本体が、{}、パーセントです。'.format( + int(self._battery_spot))) + + if self._battery_temperature > threshold_warning_battery_temperature\ + and (rospy.get_time() - self.last_warn_bat_temp_time) > 180: + rospy.logerr('Battery temperature is high. Battery temp:{:.2f} > threshold:{}' + .format(self._battery_temperature, threshold_warning_battery_temperature)) + sound_client.say('バッテリー温度が高いです。') + self.last_warn_bat_temp_time = rospy.get_time() + + def _cb_spot(self, msg): + + self._battery_spot = msg.battery_states[0].charge_percentage + self._battery_temperature = max(msg.battery_states[0].temperatures) + + def _cb_connected(self, msg): + + self._is_connected = msg.data + + def _call_estop_gentle(self): + + try: + trigger = rospy.ServiceProxy('/spot/estop/gentle', Trigger) + ret = trigger() + rospy.loginfo('Call %s and received %s(%s)'%(trigger.resolved_name, ret.sucess, ret.message)) + except rospy.ServiceException as e: + rospy.logerr('Service call failed: %s'%e) + + def _call_estop_hard(self): + + try: + trigger = rospy.ServiceProxy('/spot/estop/hard', Trigger) + ret = trigger() + rospy.loginfo('Call %s and received %s(%s)'%(trigger.resolved_name, ret.sucess, ret.message)) + except rospy.ServiceException as e: + rospy.logerr('Service call failed: %s'%e) + + def _call_go_back_home(self): + + try: + trigger = rospy.ServiceProxy('/spot/dock_fixed_id', Trigger) + ret = trigger() + rospy.loginfo('Call %s and received %s(%s)'%(trigger.resolved_name, ret.sucess, ret.message)) + except rospy.ServiceException as e: + rospy.logerr('Service call failed: %s'%e) + + +def main(): + + rospy.init_node('battery_notifier') + battery_notifier = SpotBatteryNotifier() + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/battery_visualizer.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/battery_visualizer.py new file mode 100755 index 0000000000..95be4670c5 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/battery_visualizer.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import rospy +from spot_msgs.msg import BatteryStateArray +from std_msgs.msg import Float32 + + +class SpotBatteryVisualizer(object): + + def __init__(self): + + rospy.init_node('spot_battery_visualizer') + + self._sub = rospy.Subscriber( + '/spot/status/battery_states', BatteryStateArray, self._cb) + self._pub = rospy.Publisher( + '/spot/status/battery_percentage', Float32, queue_size=1) + + def _cb(self, msg): + + pub_msg = Float32() + pub_msg.data = msg.battery_states[0].charge_percentage + self._pub.publish(pub_msg) + + +def main(): + + battery_visualizer = SpotBatteryVisualizer() + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/cable_connection_detector.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/cable_connection_detector.py new file mode 100755 index 0000000000..606c5abf36 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/cable_connection_detector.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +import rospy +import spot_msgs.msg +import sensor_msgs.msg +from std_msgs.msg import Bool + + +class CalbeConnectionDetector: + + def __init__(self): + + self.pub = rospy.Publisher( + '/spot/status/cable_connected', Bool, queue_size=1) + + self.msg_battery_spot = None + self.msg_battery_laptop = None + + self.sub_battery_spot = rospy.Subscriber( + '/spot/status/power_state', + spot_msgs.msg.PowerState, + self.callback_battery_spot) + + rate = rospy.Rate(1) + while not rospy.is_shutdown(): + rate.sleep() + if self.msg_battery_spot == None and self.msg_battery_laptop == None: + continue + self.pub.publish(Bool(self.check_connection())) + + def check_connection(self): + + if self.msg_battery_spot is not None and\ + self.msg_battery_spot.shore_power_state == spot_msgs.msg.PowerState.STATE_ON_SHORE_POWER: + return True + else: + return False + + def callback_battery_spot(self, msg): + + self.msg_battery_spot = msg + + + +def main(): + + rospy.init_node('cable_connection_detector') + node = CalbeConnectionDetector() + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/call_spot_dock_with_id.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/call_spot_dock_with_id.py new file mode 100755 index 0000000000..c21e1fec7e --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/call_spot_dock_with_id.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +import rospy +from std_srvs.srv import Trigger, TriggerResponse +from spot_msgs.srv import Dock + +class SpotDockWithID(object): + + def __init__(self): + + self._dock_id = int(rospy.get_param('~dock_id', 1)) + + self._server = rospy.Service('/spot/dock_fixed_id', Trigger, self._cb) + self._client = rospy.ServiceProxy('/spot/dock', Dock) + + def _cb(self, req): + + resp = self._client(self._dock_id) + + rospy.loginfo("Call /spot/dock(dock_id={}) returns {}".format(self._dock_id, resp)) + return TriggerResponse(success=resp.success, message=resp.message) + +def main(): + rospy.init_node('spot_dock_with_id') + end_effector_to_joy = SpotDockWithID() + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/end_effector_to_joy.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/end_effector_to_joy.py new file mode 100755 index 0000000000..e62715641d --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/end_effector_to_joy.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +import rospy +import tf2_ros +from spot_msgs.msg import ManipulatorState +from sensor_msgs.msg import Joy +from std_srvs.srv import SetBool, SetBoolResponse + +# python3 does noth have cmp... +def cmp(a, b): + return (a > b) - (a < b) + +class EndEffectorToJoy(object): + + def __init__(self): + self._tf_buffer = tf2_ros.Buffer() + self._listener = tf2_ros.TransformListener(self._tf_buffer) + self._pub = rospy.Publisher( + '/joy_head/joy_raw', + Joy) + self._sub = None + self._enabled = rospy.Service('~set_enabled', SetBool, self._set_enabled) + + self._deadzone_x = float(rospy.get_param('~deadzone_x', 0.01)) + self._deadzone_y = float(rospy.get_param('~deadzone_y', 0.01)) + self._maxrange_y = float(rospy.get_param('~maxrange_x', 0.2)) + self._maxrange_x = float(rospy.get_param('~maxrange_y', 0.2)) + self._offset_x = float(rospy.get_param('~offset_x', 0.0)) + self._offset_y = float(rospy.get_param('~offset_y', 0.0)) + + def _set_enabled(self, req): + if req.data: + self._center_x = float(rospy.get_param('~center_x', 0.576)) + self._center_y = float(rospy.get_param('~center_0', 0)) + rospy.logwarn("Enabled end_effector_to_joy with center ({}, {})".format(self._center_x, self._center_y)) + self._sub = rospy.Subscriber('/spot/status/manipulator_state', ManipulatorState, self._cb) + else: + rospy.logwarn("Disabled end_effector_to_joy with center") + if self._sub: + self._sub.unregister() + self._sub = None + return SetBoolResponse(True, 'Success') + + def _cb(self, msg): + joy = Joy() + joy.header.frame_id = rospy.get_name() + joy.header.stamp = rospy.Time.now() + x = msg.estimated_end_effector_force_in_hand.x + y = msg.estimated_end_effector_force_in_hand.y + z = msg.estimated_end_effector_force_in_hand.z + try: + trans = self._tf_buffer.lookup_transform('body', 'hand', rospy.Time(0)) + except (tf2_ros.LookupException, tf2_ros.ConnectivityException, tf2_ros.ExtrapolationException) as e: + rospy.logerr(e) + return None + x = trans.transform.translation.x - self._center_x + y = trans.transform.translation.y - self._center_y + rospy.loginfo_throttle(10, "end_effector_froce_in_hand = ({:4.1f}, {:4.1f}, {:4.1f}), stow_state = {}".format(x, y, z, msg.stow_state)) + if msg.stow_state == ManipulatorState.STOWSTATE_STOWED: + x = 0 + y = 0 + else: + sign_x = cmp(x, 0) + sign_y = cmp(y, 0) + x = abs(x) + self._offset_x + y = abs(y) + self._offset_y + if x > self._maxrange_x: x = self._maxrange_x + if y > self._maxrange_y: y = self._maxrange_y + x = 0 if x < self._deadzone_x else x - self._deadzone_x + y = 0 if y < self._deadzone_y else y - self._deadzone_y + x = sign_x * x / (self._maxrange_x - self._deadzone_x) + y = sign_y * y / (self._maxrange_y - self._deadzone_y) + joy.axes = [x, y, 0, 0, 0, 0] + rospy.loginfo_throttle(10, " ({:4.1f}, {:4.1f}, {:4.1f})".format(x, y, 0)) + joy.buttons = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] # dummy button data + self._pub.publish(joy) + +def main(): + rospy.init_node('end_effector_to_joy') + end_effector_to_joy = EndEffectorToJoy() + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/laptop_battery_visualizer.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/laptop_battery_visualizer.py new file mode 100755 index 0000000000..2969614532 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/laptop_battery_visualizer.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +import rospy +from sensor_msgs.msg import BatteryState +from std_msgs.msg import Float32 + + +class LaptopBatteryVisualizer(object): + + def __init__(self): + + rospy.init_node('spot_battery_visualizer') + + self._sub = rospy.Subscriber('/laptop_charge', BatteryState, self._cb) + self._pub = rospy.Publisher( + '/spot/status/laptop_battery_percentage', Float32, queue_size=1) + + def _cb(self, msg): + + pub_msg = Float32() + pub_msg.data = msg.percentage + self._pub.publish(pub_msg) + + +def main(): + + battery_visualizer = LaptopBatteryVisualizer() + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/sample_pick_object_in_image.l b/jsk_spot_robot/jsk_spot_startup/node_scripts/sample_pick_object_in_image.l new file mode 100755 index 0000000000..9aec1452cf --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/sample_pick_object_in_image.l @@ -0,0 +1,58 @@ +#!/usr/bin/env roseus + +(ros::load-ros-manifest "roseus") +(ros::load-ros-manifest "geometry_msgs") +(ros::load-ros-manifest "spot_msgs") + +(ros::roseus "sample_pick_object_in_image") +(setq *c* (instance ros::simple-action-client :init + "/spot/pick_object_in_image" spot_msgs::PickObjectInImageAction)) +(send *c* :wait-for-server) + +;; call-trigger-service reqrueis roseus>1.7.5 +;; https://github.com/jsk-ros-pkg/jsk_roseus/pull/705 +;; +(defun ros::call-trigger-service (srvname &key (wait nil) (timeout -1) (persistent nil)) + "Call std_srv/Trigger service. Use (setq r (call-trigger-service \"/test\" t)) (and r (send r :success)) to check if it succeed. If r is nil, it fail to find service." + (let (r) + (when (ros::wait-for-service srvname (if wait timeout 0)) + (setq r (ros::service-call srvname (instance std_srvs::TriggerRequest :init) persistent)) + (ros::ros-debug "Call \"~A\" returns \"~A\"" srvname (send r :message)) + (unless (send r :success) (ros::ros-warn "Call \"~A\" fails, it returns \"~A\"" srvname (send r :message))) + r))) + +(ros::subscribe "screenpoint" geometry_msgs::PointStamped + #'(lambda (msg) + (let ((goal (instance spot_msgs::PickObjectInImageActionGoal :init))) + (send goal :goal :image_source (ros::get-param "~image_source")) ;; required ex) "hand_color_image" + (send goal :goal :center (instance geometry_msgs::Point :init :x (send msg :point :x) :y (send msg :point :y))) + (send goal :goal :max_duration (instance ros::duration :init 15)) + (send *c* :send-goal goal)))) + +(do-until-key + (ros::rate 3) + (while (memq (send (send (*c* . ros::comm-state) :latest-goal-status) :status) ;; bugs in actionlib.l?? + (list actionlib_msgs::GoalStatus::*pending* + actionlib_msgs::GoalStatus::*preempted* + actionlib_msgs::GoalStatus::*succeeded* + actionlib_msgs::GoalStatus::*aborted*)) + (ros::ros-warn "waiting to select target object in image_view ...") + (ros::sleep) + (ros::spin-once)) + (ros::ros-warn ";; wait-for-result") + (send *c* :wait-for-result) + (if (send (send *c* :get-result) :success) + ;; if robot grab object, unstow then open gripper + (progn + (ros::call-trigger-service "/spot/unstow_arm") + (ros::call-trigger-service "/spot/gripper_open")) + (progn + ;; if robot failed to grab object, just open gripper + (ros::call-trigger-service "/spot/gripper_open"))) + (ros::call-trigger-service "/spot/stow_arm") + ) + + + + + diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/sample_walk_to_point_in_image.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/sample_walk_to_point_in_image.py new file mode 100755 index 0000000000..4ef0566282 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/sample_walk_to_point_in_image.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +import rospy +import actionlib +from geometry_msgs.msg import Point, PointStamped +from spot_msgs.msg import WalkToObjectInImageAction, WalkToObjectInImageGoal + +def cb(msg): + rospy.loginfo('screenpoint callback x={}, y={}, distance={}'.format(msg.point.x, msg.point.y, distance)) + timeout = 15 + goal = WalkToObjectInImageGoal( + image_source = image_source, + center = msg.point, + distance = distance, + max_duration = rospy.Duration(timeout) + ) + client.send_goal(goal) + client.wait_for_result(timeout=rospy.Duration(timeout+5)) + if client.get_result().success: + rospy.loginfo("succeeded") + else: + rospy.logerr("failed to walkt ot object") + +if __name__ == '__main__': + try: + rospy.init_node('sample_walk_to_object_in_image') + image_source = rospy.get_param('~image_source', 'hand_color') + distance = rospy.get_param('~distance', 0.5) + # subscribe point in screen + rospy.loginfo('start subscribe screenpoint at {}'.format(image_source)) + rospy.Subscriber('screenpoint', PointStamped, cb) + # action client for walk to point API + rospy.loginfo('conncting to walk_to_point_in_image action') + client = actionlib.SimpleActionClient('/spot/walk_to_object_in_image', WalkToObjectInImageAction) + client.wait_for_server() + # + rospy.loginfo('start main loop') + rospy.spin() + except rospy.ROSInterruptException: + ropsy.logerr("program interrupted before completion") diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/start-spot.l b/jsk_spot_robot/jsk_spot_startup/node_scripts/start-spot.l new file mode 100755 index 0000000000..204ccfaef5 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/start-spot.l @@ -0,0 +1,23 @@ +#!/usr/bin/env roseus + +(load "package://spoteus/spot-interface.l") +(unless (boundp '*spot*) (spot) (send *spot* :reset-pose)) +(unless (ros::ok) (ros::roseus "spot_eus_interface")) +(unless (boundp '*ri*) (setq *ri* (instance spot-interface :init))) +(send *ri* :claim) + +(setq ip "unknown") +(setq host (unix::gethostbyname "spotcore.jsk.imi.i.u-tokyo.ac.jp")) +(if (listp host) + (setq ip (format nil "~A . ~A . ~A . ~A" (elt (car host) 0) (elt (car host) 1) (elt (car host) 2) (elt (car host) 3)))) + +(setq info-txt (format nil "Hello, my name is ~A, my internet address is ~A, my owner is ~A and my dock id is ~A" + (ros::get-param "/robot/name") + ip + (unix::getenv "USER") + (round (ros::get-param "/spot/spot_ros/dock_id")))) +(ros::ros-info info-txt) +(send *ri* :speak info-txt :wait t) + + +(ros::exit) diff --git a/jsk_spot_robot/jsk_spot_startup/node_scripts/static_tf_republisher.py b/jsk_spot_robot/jsk_spot_startup/node_scripts/static_tf_republisher.py new file mode 100755 index 0000000000..ff5363d6c0 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/node_scripts/static_tf_republisher.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +import sys +import argparse + +import rospy +import rosbag +import tf2_ros + +from geometry_msgs.msg import TransformStamped +from tf2_msgs.msg import TFMessage + + +def main(): + + rospy.init_node('static_tf_republisher') + + topicname = '/tf_static' + + myargv = rospy.myargv(argv=sys.argv) + if len(myargv) > 1: + bagfilename = myargv[1] + else: + bagfilename = rospy.get_param("~file") + + broadcaster = tf2_ros.StaticTransformBroadcaster() + + list_messages = [] + transforms = [] + with rosbag.Bag(bagfilename, 'r') as inputbag: + for topic, msg, t in inputbag.read_messages('/tf_static'): + transforms.extend(msg.transforms) + + rospy.loginfo( + 'republish /tf_static with {} TransformStamped messages'.format(len(transforms))) + + broadcaster.sendTransform(transforms) + rospy.spin() + + +if __name__ == '__main__': + main() diff --git a/jsk_spot_robot/jsk_spot_startup/package.xml b/jsk_spot_robot/jsk_spot_startup/package.xml new file mode 100644 index 0000000000..fefef06d3d --- /dev/null +++ b/jsk_spot_robot/jsk_spot_startup/package.xml @@ -0,0 +1,55 @@ + + + jsk_spot_startup + 1.1.0 + The jsk_spot_startup package + + Kei Okada + Koki Shinjo + Koki Shinjo + + BSD + + catkin + + roslaunch + roslint + + app_manager + app_scheduler + aques_talk + audio_capture + cmd_vel_smoother + compressed_image_transport + image_transport + interactive_marker_twist_server + jsk_perception + jsk_robot_startup + jsk_tools + julius_ros + laptop_battery_monitor + robot_state_publisher + ros_speech_recognition + rosbag + roseus + roseus_smach + roslaunch + rosserial_python + rviz + rwt_app_chooser + sound_play + spot_description + spot_driver + spoteus + topic_tools + twist_mux + voice_text + xacro + xterm + + rostest + + + + + diff --git a/jsk_spot_robot/jsk_spot_user.rosinstall b/jsk_spot_robot/jsk_spot_user.rosinstall new file mode 100644 index 0000000000..1fb7336910 --- /dev/null +++ b/jsk_spot_robot/jsk_spot_user.rosinstall @@ -0,0 +1,16 @@ +# spot-ros is required for spot +## use https://github.com/cst0/spot_ros/pull/4 for Arm and gripper +- git: + local-name: spot-ros + uri: https://github.com/k-okada/spot_ros-arm.git + version: arm +## to support custom limb name, we need https://github.com/jsk-ros-pkg/jsk_model_tools/pull/249 +- git: + local-name: jsk_model_tools + uri: https://github.com/k-okada/jsk_model_tools.git + version: custom_limb +# wait for https://github.com/jsk-ros-pkg/jsk_pr2eus/pull/443 (add volume key arg for robot-interface :speak) to be released +- git: + local-name: jsk_pr2eus + uri: https://github.com/jsk-ros-pkg/jsk_pr2eus.git + version: master diff --git a/jsk_spot_robot/requirements.txt b/jsk_spot_robot/requirements.txt new file mode 100644 index 0000000000..6306e82a90 --- /dev/null +++ b/jsk_spot_robot/requirements.txt @@ -0,0 +1,6 @@ +protobuf == 3.19.4 # protobuf > 3.20 requres Python3.7 (https://github.com/protocolbuffers/protobuf/pull/9480) +bosdyn-client == 3.2.0 +bosdyn-mission == 3.2.0 +bosdyn-api == 3.2.0 +bosdyn-core == 3.2.0 +bosdyn-choreography-client == 3.2.0 diff --git a/jsk_spot_robot/spoteus/.gitignore b/jsk_spot_robot/spoteus/.gitignore new file mode 100644 index 0000000000..a1fe640a20 --- /dev/null +++ b/jsk_spot_robot/spoteus/.gitignore @@ -0,0 +1,3 @@ +spot.l +spot.urdf +spot.dae diff --git a/jsk_spot_robot/spoteus/CMakeLists.txt b/jsk_spot_robot/spoteus/CMakeLists.txt new file mode 100644 index 0000000000..23b23f0bcb --- /dev/null +++ b/jsk_spot_robot/spoteus/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 2.8.3) +project(spoteus) + +## Find catkin macros and libraries +## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) +## is used, also find other catkin packages +find_package(catkin REQUIRED COMPONENTS + roseus + euscollada +) +find_package(spot_description) # Just in case when description is not released. Avoid compile failing + +catkin_package() + +if(NOT spot_description_FOUND) + message(WARNING "spot_description is not found, so skip generating spot.l") + message(WARNING "Install spot_description from https://github.com/clearpathrobotics/spot_ros") + return() +endif() + + +########### +## Build ## +########### +if(EXISTS ${spot_description_SOURCE_PREFIX}/urdf) + set(_spot_urdf ${spot_description_SOURCE_PREFIX}/urdf) +else() + set(_spot_urdf ${spot_description_PREFIX}/share/spot_description/urdf) +endif() +set(_collada2eus ${euscollada_PREFIX}/lib/euscollada/collada2eus) + +message("spot_urdf: ${_spot_urdf}") +message("urdf_to_collada: ${_urdf_to_collada}") +message("collada2eus: ${_collada2eus}") + +add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/spot.urdf + COMMAND xacro ${_spot_urdf}/spot.urdf.xacro > ${PROJECT_SOURCE_DIR}/spot.urdf + DEPENDS ${_spot_urdf}/spot.urdf.xacro) + +add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/spot.l + COMMAND echo "${_collada2eus} spot.urdf spot.l" + COMMAND ${_collada2eus} ${PROJECT_SOURCE_DIR}/spot.urdf ${PROJECT_SOURCE_DIR}/spot.yaml ${PROJECT_SOURCE_DIR}/spot.l + DEPENDS ${PROJECT_SOURCE_DIR}/spot.urdf ${PROJECT_SOURCE_DIR}/spot.yaml ${_collada2eus}) +add_custom_target(compile_spot ALL DEPENDS ${PROJECT_SOURCE_DIR}/spot.l) + + +install(DIRECTORY demo test + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS) + +file(GLOB LISP_FILES *.l) +install(FILES ${LISP_FILES} spot.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + +if(CATKIN_ENABLE_TESTING) + find_package(catkin REQUIRED COMPONENTS rostest) + add_rostest(test/test-spot.test) +endif() diff --git a/jsk_spot_robot/spoteus/demo/sample_basics.l b/jsk_spot_robot/spoteus/demo/sample_basics.l new file mode 100755 index 0000000000..fcd8910854 --- /dev/null +++ b/jsk_spot_robot/spoteus/demo/sample_basics.l @@ -0,0 +1,80 @@ +#!/usr/bin/env roseus + +(load "package://spoteus/spot-interface.l") + +(spot-init nil) ;; do not create-viewer + +;; power on +(send *ri* :claim) +(send *ri* :power-on) + +(send *ri* :stand) +(ros::duration-sleep 1) + +;; states +(format t "(send *ri* :state :metrics) : ~A~%" (send *ri* :state :metrics)) +(format t "(send *ri* :state :leases) : ~A~%" (send *ri* :state :leases)) +(format t "(send *ri* :state :estop) : ~A~%" (send *ri* :state :estop)) +(format t "(send *ri* :state :wifi) : ~A~%" (send *ri* :state :wifi)) +(format t "(send *ri* :state :power-state) : ~A~%" (send *ri* :state :power-state)) +(format t "(send *ri* :state :battery-faults) : ~A~%" (send *ri* :state :battery-faults)) +(format t "(send *ri* :state :system-faults) : ~A~%" (send *ri* :state :system-faults)) +(format t "(send *ri* :state :feedback) : ~A~%" (send *ri* :state :feedback)) + +;; basic services +(format t "Testing Basic Functionality~%") +(send *ri* :sit) +(ros::duration-sleep 3) + +(send *ri* :power-off) +(ros::duration-sleep 3) + +(send *ri* :release) +(ros::duration-sleep 3) + +(send *ri* :claim) +(ros::duration-sleep 3) + +(send *ri* :power-on) +(ros::duration-sleep 3) + +(send *ri* :stand) +(ros::duration-sleep 5) + +;; cmd-vel +(format t "Testing cmd-vel~%") +(setq end-time (ros::time+ (ros::time 10) (ros::time-now))) +(while (ros::time< (ros::time-now) end-time) + (send *ri* :go-velocity 0 0 0.8) + ) + +(setq end-time (ros::time+ (ros::time 10) (ros::time-now))) +(while (ros::time< (ros::time-now) end-time) + (send *ri* :go-velocity 0 0 -0.8) + ) + +;; body-pose +(format t "Testing body-pose~%") +(send *ri* :body-pose '(0 0.2 0)) +(ros::duration-sleep 1) +(send *ri* :body-pose '(0 -0.2 0)) +(ros::duration-sleep 1) +(send *ri* :body-pose '(0 0 0.2)) +(ros::duration-sleep 1) +(send *ri* :body-pose '(0 0 -0.2)) +(ros::duration-sleep 1) +(send *ri* :body-pose '(0.1 0 0)) +(ros::duration-sleep 1) +(send *ri* :body-pose '(-0.1 0 0)) +(ros::duration-sleep 1) +(send *ri* :body-pose '(0 0 0)) +(ros::duration-sleep 1) + +;; go pos +(format t "Testing go pos") +(send *ri* :go-pos 1 0) +(send *ri* :go-pos 0 0 90) +(send *ri* :go-pos 0 1 -90) + +;; +(exit 1) diff --git a/jsk_spot_robot/spoteus/demo/sample_go_to_spot.l b/jsk_spot_robot/spoteus/demo/sample_go_to_spot.l new file mode 100755 index 0000000000..e623800d64 --- /dev/null +++ b/jsk_spot_robot/spoteus/demo/sample_go_to_spot.l @@ -0,0 +1,19 @@ +#!/usr/bin/env roseus + +(load "package://spoteus/spot-interface.l") + +(spot-init) +(send *ri* :stand) +;; if dock is used +;; (setq dock-id (ros::get-param "~dock_id" 520)) +;; (send *ri* :undock) + +;;(send *ri* :go-to-spot "eng2_7FElevator") +(send *ri* :go-to-spot "eng2_2FElevator") +(send *ri* :speak-jp "こんにちは" :wait t) +(send *ri* :speak-en "hello" :wait t) +(send *ri* :go-to-spot "eng2_73B2") + +(send *ri* :sit) +(send *ri* :stand) +;; (send *ri* :dock dock-id) diff --git a/jsk_spot_robot/spoteus/demo/sample_navigate_to.l b/jsk_spot_robot/spoteus/demo/sample_navigate_to.l new file mode 100755 index 0000000000..81a9b58e1e --- /dev/null +++ b/jsk_spot_robot/spoteus/demo/sample_navigate_to.l @@ -0,0 +1,38 @@ +#!/usr/bin/env roseus + +;; +;; This script is for demonstration of GraphNav interface with euslisp. +;; By default, it is assumed that Spot is at the entrance of 73B2 and headed to the AR marker on the door. +;; + +(load "package://spoteus/spot-interface.l") + +(spot-init nil) ;; do not create-viewer + +(setq *path* (ros::get-param "~path" (format nil "~A/autowalk/eng2_73B2_to_81C1.walk" (ros::rospack-find "spot_autowalk_data")))) +(setq *init-waypoint* (floor (ros::get-param "~init_waypoint" 0))) +(setq *upload* (ros::get-param "~upload" t)) + +;; Upload graphnav files to the robot. +(if *upload* (send *ri* :upload-path *path*)) + +;; Localize the robot in the map +(ros::ros-info "initialize position with waypoint of ~A" *init-waypoint*) +(send *ri* :initial-localization-waypoint *init-waypoint*) +;; you can also use following command to initialize localization if you start from 73B2 +;; (send *ri* :initial-localization-fiducial) +;; the difference is, :initial-localization-waypoint can initialize with any waypoint, meaning you can start from arbitary wapoint, e.g., -1 -> 0, 1 -> -1 + +(ros::ros-info "ready go to 81C1?") +(if (y-or-n-p) t (exit)) + +;; go to 81C1 +(send *ri* :navigate-to -1) + +(ros::ros-info "ready go back to 73B2?") +(if (y-or-n-p) t (exit)) + +;; go back to 73B2 +(send *ri* :navigate-to 0) + +(send *ri* :sit) diff --git a/jsk_spot_robot/spoteus/demo/sample_visualization.l b/jsk_spot_robot/spoteus/demo/sample_visualization.l new file mode 100755 index 0000000000..1d5e539adc --- /dev/null +++ b/jsk_spot_robot/spoteus/demo/sample_visualization.l @@ -0,0 +1,23 @@ +#!/usr/bin/env roseus + +;; +;; This script is for demonstration of visualization of spot's angle vector and global pose with irtviewer +;; + +(load "package://spoteus/spot-interface.l") + +(spot-init t) ;; create-viewer +(send *irtviewer* :draw-floor t) + +;; if not power-on, power on +;; if not stand, stand + +(do-until-key + ;; update body posture + (send *spot* :angle-vector (send *ri* :state :angle-vector)) + ;; + (send *spot* :move-to (send *ri* :state :worldcoords) :world) + (send *irtviewer* :look1 (send *spot* :worldpos)) + (send *irtviewer* :draw-objects) + (x::window-main-one) + ) diff --git a/jsk_spot_robot/spoteus/package.xml b/jsk_spot_robot/spoteus/package.xml new file mode 100644 index 0000000000..258253ca59 --- /dev/null +++ b/jsk_spot_robot/spoteus/package.xml @@ -0,0 +1,29 @@ + + + spoteus + 1.1.0 + The spoteus package + + Kei Okada + Yoshiki Obinata + BSD + Kei Okada + Yoshiki Obinata + + catkin + + collada_urdf + euscollada + rostest + roseus + pr2eus + spot_description + + pr2eus + roseus + spot_msgs + std_srvs + + + + diff --git a/jsk_spot_robot/spoteus/spot-interface.l b/jsk_spot_robot/spoteus/spot-interface.l new file mode 100644 index 0000000000..c384aa122e --- /dev/null +++ b/jsk_spot_robot/spoteus/spot-interface.l @@ -0,0 +1,553 @@ +(ros::roseus "spot") + +(require "package://spoteus/spot-utils.l") +(require "package://pr2eus/robot-interface.l") + +(ros::roseus-add-srvs "std_srvs") +(ros::roseus-add-msgs "spot_msgs") +(ros::roseus-add-srvs "spot_msgs") +(ros::roseus-add-srvs "control_msgs") + +(defun call-trigger-service (srvname &key (wait nil)) + "Call std_srv/Trigger service" + (let (r) + (when (send *ri* :simulation-modep) + (ros::ros-warn "call-trigger-service (~A)" srvname) + (return-from call-trigger-service t)) + (if wait (ros::wait-for-service srvname)) + (setq r (ros::service-call srvname (instance std_srvs::TriggerRequest :init))) + (ros::ros-debug "Call \"~A\" returns \"~A\"" srvname (send r :message)) + (send r :success))) + +(defun call-set-bool-service (srvname data &key (wait nil)) + "Call std_srv/Trigger service" + (let (r) + (when (send *ri* :simulation-modep) + (ros::ros-warn "call-set-bool-service (~A) ~A" srvname data) + (return-from call-set-bool-service t)) + (if wait (ros::wait-for-service srvname)) + (setq r (ros::service-call srvname (instance std_srvs::SetBoolRequest :init :data data))) + (ros::ros-info "Call \"~A\" returns \"~A\"" srvname (send r :message)) + (send r :success))) + +(defun call-set-locomotion-service (srvname locomotion_hint &key (wait nil)) + "Call spot_msgs/SetLocomotion service" + (let (r) + (when (send *ri* :simulation-modep) + (ros::ros-warn "call-set-locomotion-service (~A) ~A" srvname locomotion_hint) + (return-from call-set-locomotion-service t)) + (if wait (ros::wait-for-service srvname)) + (setq r (ros::service-call srvname (instance spot_msgs::SetLocomotionRequest :init :locomotion_mode locomotion_hint))) + (ros::ros-info "Call \"~A\" returns \"~A\"" srvname (send r :message)) + (send r :success))) + +(defun call-dock-service (srvname dock_id &key (wait nil)) + "Call spot_msgs/Dock service" + (let (r) + (when (send *ri* :simulation-modep) + (ros::ros-warn "call-dock-service (~A) ~A" srvname dock_id) + (return-from call-dock-service t)) + (if wait (ros::wait-for-service srvname)) + (setq r (ros::service-call srvname (instance spot_msgs::DockRequest :init :dock_id dock_id))) + (ros::ros-info "Call \"~A\" returns \"~A\"" srvname (send r :message)) + (send r :success))) + +(defclass spot-interface + :super robot-move-base-interface + :slots (trajectory-cmd-action pick-object-in-image-client pick-object-in-image-feedback-msg) + ) + +(defmethod spot-interface + (:init + (&rest args &key (trajectory-cmd-action-name "/spot/trajectory") &allow-other-keys) + (prog1 + (send-super* :init :robot spot-robot :base-frame-id "base_link" :odom-topic "/odom_combined" :base-controller-action-name nil args) + ;; check if spot_ros/driver.launch started + (unless (or (send self :simulation-modep) (ros::wait-for-service "/spot/claim" 5)) + (ros::ros-error "could not communicate with robot, may be forget to roslaunch spot_driver driver.launch, or did not power on the robot")) + ;; http://www.clearpathrobotics.com/assets/guides/melodic/spot-ros/ros_usage.html#view-the-robot + ;; spot_msgs version 0.0.0 + (ros::subscribe "/spot/status/metrics" spot_msgs::Metrics #'send self :spot-status-metrics-callback :groupname groupname) + (ros::subscribe "/spot/status/leases" spot_msgs::LeaseArray #'send self :spot-status-leases-callback :groupname groupname) + (ros::subscribe "/spot/status/feet" spot_msgs::FootStateArray #'send self :spot-status-feet-callback :groupname groupname) + (ros::subscribe "/spot/status/estop" spot_msgs::EStopStateArray #'send self :spot-status-estop-callback :groupname groupname) + (ros::subscribe "/spot/status/wifi" spot_msgs::WiFiState #'send self :spot-status-wifi-callback :groupname groupname) + (ros::subscribe "/spot/status/manipulator_state" spot_msgs::ManipulatorState #'send self :spot-status-manipulator-state-callback :groupname groupname) + (ros::subscribe "/spot/status/power_state" spot_msgs::PowerState #'send self :spot-status-power-state-callback :groupname groupname) + (ros::subscribe "/spot/status/battery_states" spot_msgs::BatteryStateArray #'send self :spot-status-battery-states-callback :groupname groupname) + (ros::subscribe "/spot/status/behavior_faults" spot_msgs::BehaviorFaultState #'send self :spot-status-behavior-faults-callback :groupname groupname) + (ros::subscribe "/spot/status/system_faults" spot_msgs::SystemFaultState #'send self :spot-status-system-faults-callback :groupname groupname) + (ros::subscribe "/spot/status/feedback" spot_msgs::Feedback #'send self :spot-feedback-callback :groupname groupname) + (setq trajectory-cmd-action (instance ros::simple-action-client :init + trajectory-cmd-action-name spot_msgs::TrajectoryAction + :groupname groupname)) + (setq pick-object-in-image-client (instance ros::simple-action-client :init + "/spot/pick_object_in_image" spot_msgs::PickObjectInImageAction)) + )) + ;; (:default-controller () + ;; (append + ;; (send self :arm-controller) + ;; (send self :dummy-controller) + ;; )) + (:default-controller () (send self :arm-controller)) + (:arm-controller () + (list + (list + (cons :controller-action "spot/arm_controller/follow_joint_trajectory") + (cons :controller-state "spot/arm_controller/follow_joint_trajectory/state") + (cons :action-type control_msgs::FollowJointTrajectoryAction) + (cons :joint-names (send-all (send robot :arm :joint-list) :name)) + ))) + (:set-impedance-param + (&key (linear-stiffness #f(500 500 500)) + (rotational-stiffness #f(60 60 60)) + (linear-damping #f(2.5 2.5 2.5)) + (rotational-damping #f(1.0 1.0 1.0))) + "X is up, Z is forward" + (let (m r) + (labels + ((v2v (v) (instance geometry_msgs::Vector3 :init :x (elt v 0) :y (elt v 1) :z (elt v 2)))) + (setq m (instance spot_msgs::SetArmImpedanceParamsRequest :init + :linear_stiffness (v2v linear-stiffness) + :rotational_stiffness (v2v rotational-stiffness) + :linear_damping (v2v linear-damping) + :rotational_damping (v2v rotational-damping))) + (setq r (ros::service-call "/spot/arm_impedance_parameters" m)) + (send r :success)))) + (:unstow-arm () (call-trigger-service "/spot/unstow_arm")) + (:stow-arm () (call-trigger-service "/spot/stow_arm")) + (:gripper-open () (call-trigger-service "/spot/gripper_open")) + (:gripper-close () (call-trigger-service "/spot/gripper_close")) + (:start-grasp + () + (let (p s) + (send self :gripper-close) + (setq p (send *ri* :state :manipulator-state-gripper-open-percentage) + s (send *ri* :state :manipulator-state-is-gripper-holding-item)) + (ros::ros-info "gripper-open-percentage ~A" p) + (ros::ros-info "manipulator-state-is-gripper-holding-item ~A" s) + s)) + (:stop-grasp + () + (let (p s) + (send self :gripper-open) + (setq p (send *ri* :state :manipulator-state-gripper-open-percentage) + s (send *ri* :state :manipulator-state-is-gripper-holding-item)) + (ros::ros-info "gripper-open-percentage ~A" p) + (ros::ros-info "manipulator-state-is-gripper-holding-item ~A" s) + s)) + (:pick-object-in-image + (image-source x y &optional (max-duration 15)) + (let ((goal (instance spot_msgs::PickObjectInImageActionGoal :init))) + (print (list image-source x y max-duration)) + (send goal :goal :image_source image-source) + (send goal :goal :center (instance geometry_msgs::Point :init :x x :y y)) + (send goal :goal :max_duration (instance ros::duration :init max-duration)) + (send pick-object-in-image-client :send-goal goal :feedback-cb `(lambda (msg) (send ,self :pick-object-in-image-feedback-cb msg))) + )) + (:pick-object-in-image-feedback-cb + (msg) + (setq pick-object-in-image-feedback-msg msg)) + (:pick-object-in-image-feedback-msg + (&rest args) + (forward-message-to pick-object-in-image-feedback-msg args)) + (:pick-object-in-image-wait-for-result + () + (send pick-object-in-image-client :wait-for-result) + (send pick-object-in-image-client :get-result)) + (:pick-object-in-image-get-state + () + (send pick-object-in-image-client :get-state)) + ;; (:dummy-controller () + ;; (list + ;; (list + ;; (cons :controller-action "spot/dummy_controller/follow_joint_trajectory") + ;; (cons :controller-state "spot/dummy_controller/follow_joint_trajectory/state") + ;; (cons :action-type control_msgs::FollowJointTrajectoryAction) + ;; (cons :joint-names (send-all (set-difference (send robot :joint-list) (send robot :arm :joint-list)) :name)) + ;; ))) + (:spot-status-metrics-callback + (msg) + (send self :set-robot-state1 :metrics-distance (send msg :distance)) + (send self :set-robot-state1 :metrics-gaint-cycles (send msg :gait_cycles)) + (send self :set-robot-state1 :metrics-time-moving (send (send msg :time_moving) :to-sec)) + (send self :set-robot-state1 :metrics-electric-power (send (send msg :electric_power) :to-sec))) + (:spot-status-leases-callback + (msg) + (dolist (resource (send msg :resources)) + (let ((r (string-upcase (send resource :resource)))) + (send self :set-robot-state1 (intern (format nil "LEASES-~A-RESOURCE" r) *keyword-package*) + (send resource :lease :resource)) + (send self :set-robot-state1 (intern (format nil "LEASES-~A-EPOCH" r) *keyword-package*) + (send resource :lease :epoch)) + (send self :set-robot-state1 (intern (format nil "LEASES-~A-SEQUENCE" r) *keyword-package*) + (send resource :lease :sequence)) + (send self :set-robot-state1 (intern (format nil "LEASES-~A-CLIENT-NAME" r) *keyword-package*) + (send resource :lease_owner :client_name)) + (send self :set-robot-state1 (intern (format nil "LEASES-~A-USER-NAME" r) *keyword-package*) + (send resource :lease_owner :user_name))))) + (:spot-status-feet-callback + (msg) + (send self :set-robot-state1 :feet + (mapcar #'(lambda (state) + (list (cons :foot-position-rt-body (ros::tf-point->pos (send state :foot_position_rt_body))) + (cons :contact (case (send state :contact) (0 'unknown) (1 'made) (2 'lost))))) (send msg :states)))) + (:spot-status-estop-callback + (msg) + (dolist (state (send msg :estop_states)) + (let ((s (string-upcase (substitute #\- #\_ (send state :name))))) + (send self :set-robot-state1 (intern (format nil "ESTOP-~A-NAME" s) *keyword-package*) + (send state :name)) + (send self :set-robot-state1 (intern (format nil "ESTOP-~A-TYPE" s) *keyword-package*) + (case (send state :type) (0 'unknown) (1 'hardware) (2 'software))) + (send self :set-robot-state1 (intern (format nil "ESTOP-~A-STATE" s) *keyword-package*) + (case (send state :state) (0 'unknown) (1 'estopped) (2 'not-estopped))) + (send self :set-robot-state1 (intern (format nil "ESTOP-~A-STATE-DESCRIPTION" s) *keyword-package*) + (send state :state_description))))) + (:spot-status-wifi-callback + (msg) + (send self :set-robot-state1 :wifi-current-mode + (case (send msg :current_mode) + (0 'unknown) (1 'access-point) (2 'client))) + (send self :set-robot-state1 :wifi-essid (send msg :essid))) + (:spot-status-manipulator-state-callback + (msg) + (send self :set-robot-state1 :manipulator-state-gripper-open-percentage (send msg :gripper_open_percentage)) + (send self :set-robot-state1 :manipulator-state-is-gripper-holding-item (send msg :is_gripper_holding_item)) + (send self :set-robot-state1 :manipulator-state-estimated-end-effector-force-in-hand + (let ((m (send msg :estimated_end_effector_force_in_hand))) + (float-vector (send m :x) (send m :y) (send m :z)))) + (send self :set-robot-state1 :manipulator-state-stow-state + (case (send msg :stow_state) (0 'unknown) (1 'stowed) (2 'deployed))) + (send self :set-robot-state1 :manipulator-state-velocity-of-hand-in-vision + (let ((m (send msg :velocity_of_hand_in_vision))) + (float-vector (send m :linear :x) (send m :linear :y) (send m :linear :z) + (send m :angular :x) (send m :angular :y) (send m :angular :z)))) + (send self :set-robot-state1 :manipulator-state-velocity-of-hand-in-odom + (let ((m (send msg :velocity_of_hand_in_odom))) + (float-vector (send m :linear :x) (send m :linear :y) (send m :linear :z) + (send m :angular :x) (send m :angular :y) (send m :angular :z)))) + (send self :set-robot-state1 :manipulator-carry-state + (case (send msg :carry_state) (0 'unknown) (1 'not-carriable) (2 'carriable) (3 'carriable-and-stowable))) + ) + (:spot-status-power-state-callback + (msg) + (send self :set-robot-state1 :power-state-motor-power-state + (case (send msg :motor_power_state) + (0 'unknown) (1 'off) (2 'on) (3 'powering-on) (4 'powering-off) (5 'error))) + (send self :set-robot-state1 :power-state-shore-power-state + (case (send msg :shore_power_state) + (0 'unknown-shore-power) (1 'on-shore-power) (2 'off-shore-power))) + (send self :set-robot-state1 :power-state-locomotion-charge-percentage (send msg :locomotion_charge_percentage)) + (send self :set-robot-state1 :power-state-locomotion-estimated-runtime (send (send msg :locomotion_estimated_runtime) :to-sec))) + (:spot-status-battery-states-callback + (msg) + (dolist (state (send msg :battery_states)) + (let ((i 1)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-IDENTIFIER" i) *keyword-package*) + (send state :identifier)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-CHARGE-PERCENTAGE" i) *keyword-package*) + (send state :charge_percentage)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-ESTIMATED-RUNTIME" i) *keyword-package*) + (send (send state :estimated_runtime) :to-sec)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-CURRENT" i) *keyword-package*) + (send state :current)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-VOLTAGE" i) *keyword-package*) + (send state :voltage)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-TEMPERATURES" i) *keyword-package*) + (send state :temperatures)) + (send self :set-robot-state1 (intern (format nil "BATTERY-STATES-~A-STATUS" i) *keyword-package*) + (case (send state :status) (0 'unknown) (1 'missing) (2 'charging) (3 'discharging) (4 'booting))) + (setq i (+ i 1)) + ))) + (:spot-status-behavior-faults-callback + (msg) + (send self :set-robot-state1 :behavior-faults + (mapcar #'(lambda (fault) + (list (cons :behavior-fault-id (send fault :behavior_fault_id)) + (cons :cause (case (send fault :cause) (0 'unknown) (1 'fall) (2 'hardware))) + (cons :status (case (send fault :status) (0 'unknown) (1 'clearable) (2 'unclearable))))) + (send msg :faults)))) + (:spot-status-system-faults-callback + (msg) + (dolist (fault (send msg :faults)) + (let ((s (string-upcase (substitute #\- #\_ (send fault :name))))) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-NAME" s) *keyword-package*) + (send fault :NAME)) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-DURATION" s) *keyword-package*) + (send (send fault :duration) :to-sec)) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-CODE" s) *keyword-package*) + (send fault :code)) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-UID" s) *keyword-package*) + (send fault :uid)) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-ERROR_MESSAGE" s) *keyword-package*) + (send fault :error_message)) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-ATTRIBUTES" s) *keyword-package*) + (send fault :attributes)) + (send self :set-robot-state1 (intern (format nil "SYSTEM-FAULTS-~A-SEVERITY" s) *keyword-package*) + (case (send fault :severity) (0 'unknown) (1 'info) (2 'warn) (3 'critical)))))) + (:spot-feedback-callback + (msg) + (send self :set-robot-state1 :feedback-standing (send msg :standing)) + (send self :set-robot-state1 :feedback-sitting (send msg :sitting)) + (send self :set-robot-state1 :feedback-moving (send msg :moving)) + (send self :set-robot-state1 :feedback-serial-number (send msg :serial_number)) + (send self :set-robot-state1 :feedback-species (send msg :species)) + (send self :set-robot-state1 :feedback-version (send msg :version)) + (send self :set-robot-state1 :feedback-nickname (send msg :nickname)) + (send self :set-robot-state1 :feedback-computer-serial-number(send msg :computer_serial_number))) + (:state + (&rest args) + "use :metrics, :leases :feet, :estop, :wifi, :power-state, :battery-states, :behvior-faults, :system-fault, :feedback to get spot status, you can also acess to the specific data by concatenating these method name + key value, for example :metrics-time-moving" + (prog1 + (send-message* self robot-interface :state args) + (flet ((gen-status + (key) + (mapcan #'(lambda (x) (if (substringp (string key) (string (car x))) (list (cons (intern (subseq (string (car x)) (1+ (length (string key)))) *keyword-package*) (cdr x))))) robot-state))) + (case (car args) + (:metrics (return-from :state (gen-status :metrics))) + (:leases (return-from :state (gen-status :leases))) + ;; (:feet (return-from :state (gen-status :feet))) + (:estop (return-from :state (gen-status :estop))) + (:wifi (return-from :state (gen-status :wifi))) + (:manipulator-state (return-from :state (gen-status :manipulator-state))) + (:power-state (return-from :state (gen-status :power-state))) + (:battery-states (return-from :state (gen-status :battery-states))) + ;; (:behavior-faults (return-from :state (gen-status :behavior-faults))) + (:system-faults (return-from :state (gen-status :system-faults))) + (:feedback (return-from :state (gen-status :feedback))) + (:angle-vector + (return-from :state (send robot :angle-vector))) + (:worldcoords + (unless joint-action-enable + (return-from :state (send self :worldcoords))) + (return-from :state (send *tfl* :lookup-transform "vision" base-frame-id (ros::time 0)))))))) + ;; + (:estop-gentle () (call-trigger-service "/spot/estop/gentle")) + (:estop-hard () (call-trigger-service "/spot/estop/hard")) + (:claim () + "Claim the robot control" + (let ((client (send self :state :leases-body-client-name))) + (if (or (null client) (string= client "")) + (call-trigger-service "/spot/claim") + (ros::ros-warn "robot is already claimed by ~A" client)))) + (:release () "Relase the robot control" (call-trigger-service "/spot/release")) + (:power-on () + "Power on the robot" + (if (send *ri* :simulation-modep) + (send self :set-robot-state1 :power-state-motor-power-state 'on) + (call-trigger-service "/spot/power_on"))) + (:power-off + () + "Power off the robot" + (if (send *ri* :simulation-modep) + (send self :set-robot-state1 :power-state-motor-power-state 'off) + (call-trigger-service "/spot/power_off"))) + (:self-right () (call-trigger-service "/spot/self_right")) + (:stand () "Stand the robot up" (call-trigger-service "/spot/stand")) + (:sit () "Sit the robot down" (call-trigger-service "/spot/sit")) + (:stop () "Stop the robot in place with minimal motion" (call-trigger-service "/spot/stop")) + (:set-locomotion-mode (locomotion-hint) "Set locomotion mode" (call-set-locomotion-service "/spot/locomotion_mode" locomotion-hint)) + (:set-stair-mode (is-stair-mode) "Set stair mode" (call-set-bool-service "/spot/stair_mode" is-stair-mode)) + ;; + (:dock (dock-id) "Dock the robot" (call-dock-service "/spot/dock" dock-id)) + (:undock + () + "Undock the robot" + (send self :power-on) + (call-trigger-service "/spot/undock")) + ;; + (:send-cmd-vel-raw + (x y d &key (topic-name "/spot/cmd_vel")) + (when (send self :simulation-modep) + (return-from :send-cmd-vel-raw t)) + (unless (ros::get-topic-publisher topic-name) + (ros::advertise topic-name geometry_msgs::Twist 1) + (unix:sleep 1)) + (let ((msg (instance geometry_msgs::Twist :init))) + (send msg :linear :x x) + (send msg :linear :y y) + (send msg :angular :z d) + (ros::publish topic-name msg))) + (:go-velocity + (x y d ;; [m/sec] [m/sec] [rad/sec] + &optional (msec 1000) ;; msec is total animation time [msec] + &key (stop t) (wait t)) + "control the robot velocity x([m/sec]) y([m/sec]) d([rad/sec]) msec([msec]). msec is the time to travel." + (unless wait + (ros::ros-error ":go-velocity without wait is unsupported") + (return-from :go-velocity nil)) + (ros::rate 100) + (let ((start-time (ros::time-now))) + (while (and (ros::ok) + (< (* 1000.0 (send (ros::time- (ros::time-now) start-time) :to-sec)) msec)) + (send self :spin-once) + (send self :send-cmd-vel-raw x y d) + (ros::sleep))) + (when stop + (send self :send-cmd-vel-raw 0 0 0)) + (ros::rate 10) + t) + (:go-pos + (x y &optional (d 0) &key (timeout 10) (wait t)) ;; [m] [m] [degree] + "move robot toward (x, y, d) (units are m, m and degrees respectively)." + ;; + (setq trajectory-cmd-goal-msg (instance spot_msgs::TrajectoryGoal :init)) + (send trajectory-cmd-goal-msg :target_pose :header :stamp (ros::time-now)) + (send trajectory-cmd-goal-msg :target_pose :header :frame_id "body") + (send trajectory-cmd-goal-msg :target_pose :pose :position :x x) + (send trajectory-cmd-goal-msg :target_pose :pose :position :y y) + (send trajectory-cmd-goal-msg :target_pose :pose :orientation :z (sin (/ (deg2rad d) 2))) + (send trajectory-cmd-goal-msg :target_pose :pose :orientation :w (cos (/ (deg2rad d) 2))) + (send trajectory-cmd-goal-msg :duration :data (ros::time timeout)) + ;; + (send trajectory-cmd-action :send-goal trajectory-cmd-goal-msg) + (if wait + (send trajectory-cmd-action :wait-for-result) + ) + ) + (:go-pos-wait + (x y &optional (d 0) (timeout 10)) ;; [m] [m] [degree] + "move robot toward (x, y, d) (units are m, m and degrees respectively). and wait" + (send self :go-pos x y d :timeout timeout :wait t) + ) + (:go-pos-no-wait + (x y &optional (d 0) (timeout 10)) ;; [m] [m] [degree] + "move robot toward (x, y, d) (units are m, m and degrees respectively)." + (send self :go-pos x y d :timeout timeout :wait nil) + ) + (:body-pose + ;; pose expected to get 3 elements float-vector (r p y), or eus coords. + (pose &key (topic-name "/spot/body_pose")) + (when (send self :simulation-modep) + (return-from :body-pose t)) + (unless (ros::get-topic-publisher topic-name) + (ros::advertise topic-name geometry_msgs::Pose 1) + (unix:sleep 1)) + (if (or (vectorp pose) (listp pose)) + (progn + (ros::ros-debug "Got r p y float-vector or list as args.") + (let ((pose-msg (instance geometry_msgs::Pose :init)) + (quaternion-msg (send self :create-quaternion-msg-from-rpy (elt pose 0) (elt pose 1) (elt pose 2)))) + (send pose-msg :orientation quaternion-msg) + (ros::publish topic-name pose-msg))) + (progn + (ros::ros-debug "Got coords variable as args.") + (let ((pose-msg (ros::coords->tf-pose pose))) + (ros::publish topic-name pose-msg)))) + (send self :stand)) + (:find-waypoint-position-from-id + (id ids) + (let (ret) + (setq ret (position id ids :test #'string=)) + ret)) + (:find-waypoint-id-from-position + (index ids) + "return waypint id from position, if you set -1 to index, it returns last waypoint" + (let (ret) + (if (< index 0) + (setq index (+ (length ids) index))) + (if (< index (length ids)) + (setq ret (elt ids index))) + ret)) + (:initial-localization-fiducial + () + "initial the localization for autowalk based on the fiducial marker" + (let (r) + (setq r (ros::service-call "/spot/set_localization_fiducial" + (instance spot_msgs::SetLocalizationFiducialRequest :init))) + (ros::ros-info "Call \"/spot/set_localization_fiducial\" returns \"~A\"" (send r :message)) + t)) + (:initial-localization-waypoint + (init-waypoint) + "initial the localization for waypoint id in the graph" + (let (r) + (if (numberp init-waypoint) + (prog1 + (setq ids (send self :list-graph)) + (setq init-waypoint (send self :find-waypoint-id-from-position init-waypoint ids)))) + (setq r (ros::service-call "/spot/set_localization_waypoint" + (instance spot_msgs::SetLocalizationWaypointRequest :init :waypoint_id init-waypoint))) + (ros::ros-info "Call \"/spot/set_localization_waypoint\" returns \"~A\"" (send r :message)) + t)) + (:upload-path + (filepath &key (initial-localization-fiducial t) (wait nil)) + "upload graph for autowalk" + (let (r) + (if wait (ros::wait-for-service srvname)) + (setq r (ros::service-call "/spot/upload_graph" + (instance spot_msgs::UploadGraphRequest :init :upload_filepath filepath))) + (ros::ros-info "Call \"/spot/upload_graph\" returns .. \"~A\"" (send r :message)) + (if initial-localization-fiducial (send self :initial-localization-fiducial)) + (send r :message))) + (:list-graph + () + "list up the waypoint (a string type hash id) list in the uploaded graph" + (let (r) + (setq r (ros::service-call "/spot/list_graph" + (instance spot_msgs::ListGraphRequest :init))) + (ros::ros-info "Call \"/spot/list_graph\" returns ..") + (dolist (id (send r :waypoint_ids)) + (ros::ros-info " \"~A\"" id)) + (send r :waypoint_ids))) + (:navigate-to + (navigate-to &key (initial-localization-waypoint nil)) + (let (ids c goal ret) + (setq ids (send self :list-graph)) + (if (numberp navigate-to) + (setq navigate-to (send self :find-waypoint-id-from-position navigate-to ids))) + (if (numberp initial-localization-waypoint) + (setq initial-localization-waypoint (send self :find-waypoint-id-from-position initial-localization-waypoint ids))) + (setq c (instance ros::simple-action-client :init + "/spot/navigate_to" spot_msgs::NavigateToAction)) + (send c :wait-for-server) + (ros::ros-info "initial waypoit ~3D/~3D ~A" + (if initial-localization-waypoint + (position initial-localization-waypoint ids :test #'string=) + -1) + (length ids) + initial-localization-waypoint) + (ros::ros-info " goal waypoit ~3D/~3D ~A" + (position navigate-to ids :test #'string=) (length ids) navigate-to) + (setq goal (instance spot_msgs::NavigateToActionGoal :init)) + (send goal :goal :id_navigate_to navigate-to) + (send c :send-goal goal :feedback-cb #'(lambda (msg) (let ((id (send msg :feedback :waypoint_id))) (ros::ros-info "~A/~A ~A" (position id ids :test #'string=) (length ids) id)))) + (setq ret (send c :wait-for-result)) + (ros::ros-info "~A ~A" ret (send (send c :get-result) :message)) + ret)) + ;; These are the methods to calculate rpy -> quaternion. + ;; You can use ros::create-quaternion-from-rpy after https://github.com/jsk-ros-pkg/jsk_roseus/pull/662 was merged and released. + (:create-quaternion-from-rpy + (roll pitch yaw) ;; return [w x y z] + (let ((sin-roll (sin (* roll 0.5))) (cos-roll (cos (* roll 0.5))) + (sin-pitch (sin (* pitch 0.5))) (cos-pitch (cos (* pitch 0.5))) + (sin-yaw (sin (* yaw 0.5))) (cos-yaw (cos (* yaw 0.5)))) + (float-vector (+ (* cos-roll cos-pitch cos-yaw) (* sin-roll sin-pitch sin-yaw)) + (- (* sin-roll cos-pitch cos-yaw) (* cos-roll sin-pitch sin-yaw)) + (+ (* cos-roll sin-pitch cos-yaw) (* sin-roll cos-pitch sin-yaw)) + (- (* cos-roll cos-pitch sin-yaw) (* sin-roll sin-pitch cos-yaw))))) + (:create-quaternion-msg-from-rpy + (roll pitch yaw) + (let* ((q (send self :create-quaternion-from-rpy roll pitch yaw)) + (qx (elt q 1)) (qy (elt q 2)) (qz (elt q 3)) (qw (elt q 0))) + (instance geometry_msgs::quaternion :init :x qx :y qy :z qz :w qw))) + ) + +(defun spot-init (&optional (create-viewer)) + (unless (boundp '*spot*) (spot) (send *spot* :reset-pose)) + (unless (ros::ok) (ros::roseus "spot_eus_interface")) + (unless (boundp '*ri*) (setq *ri* (instance spot-interface :init))) + + (ros::spin-once) + (send *ri* :spin-once) + (send *ri* :claim) + (while (member (send *ri* :state :power-state-motor-power-state) (list 'off nil)) + (unix::sleep 1) + (ros::ros-info "powering on...") + (send *ri* :power-on)) + ;; + (unless (every #'(lambda (x) (eq x 'made)) (mapcar #'(lambda (x) (cdr (assoc :contact x))) (send *ri* :state :feet))) + (ros::ros-info "run (send *ri* :stand) to stand the robot")) + + (when create-viewer (objects (list *spot*))) + ) diff --git a/jsk_spot_robot/spoteus/spot-utils.l b/jsk_spot_robot/spoteus/spot-utils.l new file mode 100644 index 0000000000..86e7b92b1b --- /dev/null +++ b/jsk_spot_robot/spoteus/spot-utils.l @@ -0,0 +1,143 @@ +(require :spot "package://spoteus/spot.l") + +(unless (assoc :init-orig (send spot-robot :methods)) + (rplaca (assoc :init (send spot-robot :methods)) :init-orig)) + +(defmethod spot-robot + (:init + (&rest args) ;; fix colors + (dolist (b (list :_make_instance_body_geom0 + :_make_instance_front_left_upper_leg_geom0 + :_make_instance_front_left_lower_leg_geom0 + :_make_instance_front_right_upper_leg_geom0 + :_make_instance_front_right_lower_leg_geom0 + :_make_instance_rear_left_upper_leg_geom0 + :_make_instance_rear_left_lower_leg_geom0 + :_make_instance_rear_right_upper_leg_geom0 + :_make_instance_rear_right_lower_leg_geom0)) + (rplacd (assoc b (send (class self) :methods)) + (cdr (subst '(list :diffuse #f(1.00 0.84 0.32 0)) + '(list :diffuse (float-vector 1.000000 1.000000 1.000000 1.000000)) + (assoc b (send (class self) :methods))))) + ) + (dolist (b (list :_make_instance_arm0.link_sh0_geom0 + :_make_instance_arm0.link_sh1_geom0 + :_make_instance_arm0.link_hr0_geom0 +; :_make_instance_arm0.link_el0_geom0 + :_make_instance_arm0.link_el1_geom0 + :_make_instance_arm0.link_wr0_geom0 + :_make_instance_arm0.link_wr1_geom0 + :_make_instance_arm0.link_fngr_geom0 + )) + (rplacd (assoc b (send (class self) :methods)) + (cdr (subst '(list :diffuse #f(1.00 0.84 0.32 0)) + '(list :diffuse (float-vector 0.200000 0.200000 0.200000 1.000000)) + (assoc b (send (class self) :methods))))) + ) + (dolist (b (list :_make_instance_front_left_hip_geom0 + :_make_instance_front_right_hip_geom0 + :_make_instance_rear_left_hip_geom0 + :_make_instance_rear_right_hip_geom0)) + (rplacd (assoc b (send (class self) :methods)) + (cdr (subst '(list :diffuse #f(0.1 0.1 0.1 0)) + '(list :diffuse (float-vector 1.000000 1.000000 1.000000 1.000000)) + (assoc b (send (class self) :methods))))) + ) + (prog1 + (send* self :init-orig args) + ;; add head-end-coords (camera coords) + (let ((head-end-coords (make-cascoords :coords (send arm-end-coords :copy-worldcoords)))) + (send head-end-coords :rotate pi/2 :y) + (send (send arm-end-coords :parent) :assoc head-end-coords) + (send self :put :head-end-coords head-end-coords)))) + ;; add head method so that (send *spot* :head :look-at) works + (:head + (&rest args) + (unless args (setq args (list nil))) + (case (car args) + (:end-coords + (user::forward-message-to (send self :get :head-end-coords) (cdr args))) + (:look-at + (let ((target-coords + (orient-coords-to-axis ;; orient target-coords to look-at direction + (make-coords :pos (cadr args)) + (v- (cadr args) (send self :head :end-coords :worldpos)))) + (head-coords (send self :head :end-coords :copy-worldcoords))) + ;; align target-coords x (image-y) direction to head direction + (send target-coords :rotate + (acos (v. (send target-coords :axis :x) + (send head-coords :axis :x))) :z) + (send* self :head :inverse-kinematics target-coords + :move-target (send self :head :end-coords) + :rotation-axis t + :translation-axis nil + :target-coords target-coords ;; for debug-view + (cddr args)))) + ;;(send* self :inverse-kinematics-loop-for-look-at :head (cdr args))) + (t + (send* self :limb :arm args)))) + (:legs ;; support legs for all limbs + (&rest args) + (case (car args) + (:crotch-r + (forward-message-to front_left_hip_x_jt (cdr args)) + (forward-message-to front_right_hip_x_jt (cdr args)) + (forward-message-to rear_left_hip_x_jt (cdr args)) + (forward-message-to rear_right_hip_x_jt (cdr args))) + (:crotch-p + (forward-message-to front_left_hip_y_jt (cdr args)) + (forward-message-to front_right_hip_y_jt (cdr args)) + (forward-message-to rear_left_hip_y_jt (cdr args)) + (forward-message-to rear_right_hip_y_jt (cdr args))) + (:knee-p + (forward-message-to front_left_knee_jt (cdr args)) + (forward-message-to front_right_knee_jt (cdr args)) + (forward-message-to rear_left_knee_jt (cdr args)) + (forward-message-to rear_right_knee_jt (cdr args))))) + (:body-inverse-kinematics + (target-coords + &rest args + &key (move-target) (link-list) + (min (float-vector -500 -500 -500 -200 -200 -100)) + (max (float-vector 500 500 500 200 200 100)) + (target-centroid-pos) + (rotation-axis) + (root-link-virtual-joint-weight #f(0.0 0.0 0.1 0.1 0.5 0.5)) + &allow-other-keys) + "The purpose of this function is to use :fullbody-inverse-kinematics by only specifying body target-coords. +Example: +(send *spot* :body-inverse-kinematics (make-coords :pos #f(0 0 50))) +" + (let ((body-coords (make-cascoords :coords (send (send self :coords) :copy-worldcoords)))) + (send body_lk :assoc body-coords) + (setq target-coords + (list target-coords + (send self :larm :end-coords :copy-worldcoords) + (send self :rarm :end-coords :copy-worldcoords) + (send self :lleg :end-coords :copy-worldcoords) + (send self :rleg :end-coords :copy-worldcoords))) + (unless move-target + (setq move-target + (list body-coords + (send self :larm :end-coords) + (send self :rarm :end-coords) + (send self :lleg :end-coords) + (send self :rleg :end-coords)))) + (unless link-list + (setq link-list + (mapcar #'(lambda (limb) (send self :link-list (send limb :parent))) move-target))) + (unless rotation-axis + (setq rotation-axis (list t nil nil nil nil))) + (prog1 + (send-super* + :fullbody-inverse-kinematics target-coords + :move-target move-target + :link-list link-list + :min min + :max max + :target-centroid-pos target-centroid-pos + :rotation-axis rotation-axis + :root-link-virtual-joint-weight root-link-virtual-joint-weight + args) + (send body_lk :dissoc body-coords)))) + ) diff --git a/jsk_spot_robot/spoteus/spot.yaml b/jsk_spot_robot/spoteus/spot.yaml new file mode 100644 index 0000000000..1157a69b53 --- /dev/null +++ b/jsk_spot_robot/spoteus/spot.yaml @@ -0,0 +1,54 @@ +## +## - collada_joint_name : euslisp_joint_name (start with :) +## + +lfleg: + - front_left_hip_x : lfleg-crotch-r + - front_left_hip_y : lfleg-crotch-p + - front_left_knee : lfleg-knee-p +rfleg: + - front_right_hip_x : rfleg-crotch-r + - front_right_hip_y : rfleg-crotch-p + - front_right_knee : rfleg-knee-p +lrleg: + - rear_left_hip_x : lrleg-crotch-r + - rear_left_hip_y : lrleg-crotch-p + - rear_left_knee : lrleg-knee-p +rrleg: + - rear_right_hip_x : rrleg-crotch-r + - rear_right_hip_y : rrleg-crotch-p + - rear_right_knee : rrleg-knee-p +arm: + - arm0.sh0 : arm-shoulder-y + - arm0.sh1 : arm-shoulder-p + - arm0.el0 : arm-elbow-p + - arm0.el1 : arm-elbow-r + - arm0.wr0 : arm-wrist-p + - arm0.wr1 : arm-wrist-r +hand: + - arm0.f1x : hand-gripper-p + + +angle-vector: + reset-pose : [0.0, 45.0, -90.0, 0.0, 45.0, -90.0, 0.0, 45.0, -90.0, 0.0, 45.0, -90.0, 0.0, -170.0, 160.0, 0.0, 10.0, 0.0, 0] + +lfleg-end-coords: + parent : front_left_lower_leg + translate : [0, 0, -0.38] + rotate : [0, 1, 0, 0] +rfleg-end-coords: + parent : front_right_lower_leg + translate : [0, 0, -0.38] + rotate : [0, 1, 0, 0] +lrleg-end-coords: + parent : rear_left_lower_leg + translate : [0, 0, -0.38] + rotate : [0, 1, 0, 0] +rrleg-end-coords: + parent : rear_right_lower_leg + translate : [0, 0, -0.38] + rotate : [0, 1, 0, 0] +arm-end-coords: + parent: arm0.link_wr1 + translate : [0.175, 0, -0.03] + rotate : [1, 0, 0, 0] diff --git a/jsk_spot_robot/spoteus/test/test-spot.l b/jsk_spot_robot/spoteus/test/test-spot.l new file mode 100755 index 0000000000..1d378779a4 --- /dev/null +++ b/jsk_spot_robot/spoteus/test/test-spot.l @@ -0,0 +1,37 @@ +#!/usr/bin/env roseus +(require :unittest "lib/llib/unittest.l") +(require "package://spoteus/spot-utils.l") +(require "package://spoteus/spot-interface.l") + +(init-unit-test) + +(deftest test-pose + (let (robot) + (setq robot (instance spot-robot :init)) + ;; move robot arm to #f(600 0 600) relative to body + (send robot :arm :inverse-kinematics (send (make-coords :pos #f(600 0 600)) :transform robot)) + )) + +(defun look-at (robot) + (let ((targets (list (make-cube 100 100 100 :pos #f(2000 0 0)) + (make-cube 100 100 100 :pos #f(2000 1000 2000)) + (make-cube 100 100 100 :pos #f(2000 -1000 1000)) + (make-cube 100 100 100 :pos #f(0 2000 2000)) + (make-cube 100 100 100 :pos #f(0 -2000 1000))))) + (objects (append (list robot) targets)) + (dolist (target targets) + (send robot :head :look-at (send target :worldpos) :debug-view :no-mesage) + (send *irtviewer* :draw-objects)))) + +(deftest test-spot-look-at + (let (robot) + (setq robot (instance spot-robot :init)) + (look-at robot) + (send robot :arm :inverse-kinematics (send (make-coords :pos #f(600 0 600)) :transform robot)) + (look-at robot))) + +(deftest test-spot-init + (spot-init)) + +(run-all-tests) +(exit) diff --git a/jsk_spot_robot/spoteus/test/test-spot.test b/jsk_spot_robot/spoteus/test/test-spot.test new file mode 100644 index 0000000000..b2e8fb2164 --- /dev/null +++ b/jsk_spot_robot/spoteus/test/test-spot.test @@ -0,0 +1,3 @@ + + + diff --git a/jsk_unitree_robot/jsk_unitree_startup/CMakeLists.txt b/jsk_unitree_robot/jsk_unitree_startup/CMakeLists.txt index e7595dd01f..6b1c3d909a 100644 --- a/jsk_unitree_robot/jsk_unitree_startup/CMakeLists.txt +++ b/jsk_unitree_robot/jsk_unitree_startup/CMakeLists.txt @@ -14,7 +14,15 @@ catkin_package() ############# ## Install ## ############# -install(DIRECTORY config launch +file(GLOB_RECURSE SCRIPT_PROGRAMS scripts/*) +foreach(SCRIPT_PROGRAM ${SCRIPT_PROGRAMS}) + if("${SCRIPT_PROGRAM}" MATCHES ".*\\.py$") + catkin_install_python(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + else() + install(PROGRAMS ${SCRIPT_PROGRAM} DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts) + endif() +endforeach() +install(DIRECTORY config launch apps autostart ino DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) diff --git a/jsk_unitree_robot/unitreeeus/CMakeLists.txt b/jsk_unitree_robot/unitreeeus/CMakeLists.txt index ad64f874f6..f541e2a509 100644 --- a/jsk_unitree_robot/unitreeeus/CMakeLists.txt +++ b/jsk_unitree_robot/unitreeeus/CMakeLists.txt @@ -44,12 +44,14 @@ foreach(ROBOT_TYPE go1) add_custom_target(compile_${ROBOT_TYPE} ALL DEPENDS ${PROJECT_SOURCE_DIR}/${ROBOT_TYPE}.l ${PROJECT_SOURCE_DIR}/${ROBOT_TYPE}-simple.l) endforeach() - +### +### install +### install(DIRECTORY test DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS) -install(FILES go1.l unitree-interface.l go1-utils.l DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) +install(FILES go1.l unitree-interface.l go1-utils.l go1.yaml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) if(CATKIN_ENABLE_TESTING) find_package(catkin REQUIRED COMPONENTS rostest)