Skip to content

Commit

Permalink
Enable kxreus for using model server
Browse files Browse the repository at this point in the history
  • Loading branch information
iory committed Feb 27, 2024
1 parent cb64f13 commit 3f88f72
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 20 deletions.
Empty file removed ros/kxreus/CATKIN_IGNORE
Empty file.
23 changes: 5 additions & 18 deletions ros/kxreus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ find_package(catkin REQUIRED
kxr_models
)


if(EXISTS ${kxr_models_SOURCE_PREFIX}/urdf/kxrl2l2a6h2m.urdf)
set(kxr_urdf ${kxr_models_SOURCE_PREFIX}/urdf/kxrl2l2a6h2m.urdf)
else()
message(WARNING "Could not found kxr_models")
endif()

catkin_package(
DEPENDS
LIBRARIES ${PROJECT_NAME}
Expand All @@ -21,18 +14,12 @@ catkin_package(
catkin_generate_virtualenv(
INPUT_REQUIREMENTS requirements.in
PYTHON_INTERPRETER python3
USE_SYSTEM_PACKAGES FALSE
USE_SYSTEM_PACKAGES TRUE
ISOLATE_REQUIREMENTS FALSE
CHECK_VENV FALSE
)

set(_urdf2eus "${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_SHARE_DESTINATION}/venv/bin/urdf2eus")
set(_venv_setup "${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_SHARE_DESTINATION}/venv/bin/activate")
set(_yaml_path ${PROJECT_SOURCE_DIR}/config/kxrl2l2a6h2m.yaml)

add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/kxrl2l2a6h2m.l
COMMAND echo "${_urdf2eus} ${kxr_urdf} ${PROJECT_SOURCE_DIR}/kxrl2l2a6h2m.l"
COMMAND . ${_venv_setup} && ${_urdf2eus} ${kxr_urdf} ${PROJECT_SOURCE_DIR}/models/kxrl2l2a6h2m.l --yaml-path ${_yaml_path}
DEPENDS ${kxr_urdf} ${_urdf2eus} ${_venv_setup} ${_yaml_path})

add_custom_target(compile_kxr ALL DEPENDS ${PROJECT_SOURCE_DIR}/kxrl2l2a6h2m.l ${PROJECT_NAME}_generate_virtualenv)
file(GLOB PYTHON_SCRIPT_FILES node_scripts/*)
catkin_install_python(
PROGRAMS ${PYTHON_SCRIPT_FILES}
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
115 changes: 115 additions & 0 deletions ros/kxreus/euslisp/kxr-interface.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
(require :robot-interface "package://pr2eus/robot-interface.l")

(ros::load-ros-manifest "control_msgs")
(ros::load-ros-manifest "kxr_controller")


(defun load-robot-model (&key (port 8000)
(namespace nil))
(let* ((port "8000")
(hash (if namespace (format nil "~A~A" namespace (ros::get-param "/eusmodel_hash"))
(ros::get-param "/eusmodel_hash")))
(robot-name (if namespace (format nil "~A~A" namespace (ros::get-param "/eus_robot_name"))
(ros::get-param "/eus_robot_name")))
(outpath (ros::resolve-ros-path (format nil "package://kxreus/models/cache/~A.l" hash)))
(server-url (format nil "http://~A:~A/~A.l"
(ros::get-host)
port
hash)))
(when (not (probe-file outpath))
(while (not (= (unix:system (format nil "wget -O ~A ~A"
outpath
server-url
))
0))
(ros::ros-info (format nil "Waiting model file from server url ~A" server-url))
(unix::usleep (* 1000 1000))))
(load outpath)
(funcall (read-from-string robot-name))))


(defclass kxr-interface
:super robot-interface
:slots (joint-names servo-on-off-client))


(defmethod kxr-interface
(:init (robot &rest args &key (type :default-controller) &allow-other-keys)
(let* ((namespace (cadr (memq :namespace args)))
(joint-param (if namespace (format nil "~A/~A" namespace "/kxr_fullbody_controller/joints")
"/kxr_fullbody_controller/joints")))
(setq joint-names (ros::get-param joint-param nil))
(while (and (ros::ok) (null joint-names))
(ros::ros-info (format nil "Waiting ~A rosparam set" joint-param))
(setq joint-names (ros::get-param joint-param nil))
(ros::sleep)))

(send-super* :init :robot robot :type type
:groupname "kxr_interface"
args)

(setq servo-on-off-client (instance ros::simple-action-client :init
(if namespace (format nil "~A/~A" namespace "/kxr_fullbody_controller/servo_on_off") "/kxr_fullbody_controller/servo_on_off")
kxr_controller::ServoOnOffAction
:groupname groupname))
(dolist (action (list servo-on-off-client))
(unless (and joint-action-enable (send action :wait-for-server 3))
(setq joint-action-enable nil)
(ros::ros-warn "~A is not respond, kxr-interface is disabled" action)
(return)))
t)
(:default-controller
()
(send self :fullbody-controller))
(:fullbody-controller
()
(list
(list
(cons :controller-action "kxr_fullbody_controller/follow_joint_trajectory")
(cons :controller-state "kxr_fullbody_controller/state")
(cons :action-type control_msgs::FollowJointTrajectoryAction)
(cons :joint-names joint-names))))
(:servo-on
(&key (names nil))
(unless joint-action-enable
(if viewer (send self :draw-objects))
(return-from :servo-on t))
(when (null names)
(setq names joint-names))
(let* (goal result)
;; send current angle-vector
(send self :angle-vector (send self :state :potentio-vector) 0.1)
(send self :wait-interpolation)

(setq goal (instance kxr_controller::ServoOnOffGoal :init))
(send goal :joint_names names)
(send goal :servo_on_states (make-array (length names) :initial-element t))
(send servo-on-off-client :send-goal goal)))
(:servo-off
(&key (names nil))
(unless joint-action-enable
(if viewer (send self :draw-objects))
(return-from :servo-off t))
(when (null names)
(setq names joint-names))
(let* (goal result)
(setq goal (instance kxr_controller::ServoOnOffGoal :init))
(send goal :joint_names names)
(send goal :servo_on_states (make-array (length names) :initial-element nil))
(send servo-on-off-client :send-goal goal))))


(defun kxr-init (&key
(namespace nil)
(create-viewer t))
(unless (boundp '*robot*)
(setq *robot* (load-robot-model :namespace namespace
:port (ros::get-param (if namespace (format nil "~A/port" namespace) "/port") 8000))))
(unless (ros::ok) (ros::roseus "kxr_eus_interface"))
(unless (boundp '*ri*)
(setq *ri* (instance kxr-interface :init *robot* :namespace namespace)))

(ros::spin-once)
(send *ri* :spin-once)
(send *robot* :angle-vector (send *ri* :angle-vector))
(when create-viewer (objects (list *robot*))))
32 changes: 32 additions & 0 deletions ros/kxreus/launch/eusmodel_server.launch
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<launch>

<arg name="namespace" default="" />
<arg name="port" default="8000" />

<group if="$(eval len(arg('namespace')) > 0)" ns="$(arg namespace)" >
<node name="eusmodel_server"
pkg="kxreus" type="eus_model_server.py" >
</node>

<node name="http_server_node"
pkg="kxreus" type="http_server_node.py" >
<rosparam subst_value="true" >
port: $(arg port)
</rosparam>
</node>
</group>

<group unless="$(eval len(arg('namespace')) > 0)">
<node name="eusmodel_server"
pkg="kxreus" type="eus_model_server.py" >
</node>

<node name="http_server_node"
pkg="kxreus" type="http_server_node.py" >
<rosparam subst_value="true" >
port: $(arg port)
</rosparam>
</node>
</group>

</launch>
93 changes: 93 additions & 0 deletions ros/kxreus/node_scripts/eus_model_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python

import hashlib
import os
import tempfile

from filelock import FileLock
import rospkg
import rospy
from skrobot.model import RobotModel
from skrobot.utils.urdf import no_mesh_load_mode
from urdfeus.urdf2eus import urdf2eus


def checksum_md5(filename, blocksize=8192):
"""Calculate md5sum.
Parameters
----------
filename : str or pathlib.Path
input filename.
blocksize : int
MD5 has 128-byte digest blocks (default: 8192 is 128x64).
Returns
-------
md5 : str
calculated md5sum.
"""
filename = str(filename)
hash_factory = hashlib.md5()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(blocksize), b''):
hash_factory.update(chunk)
return hash_factory.hexdigest()


class EusModelServer(object):

def __init__(self):
full_namespace = rospy.get_namespace()
last_slash_pos = full_namespace.rfind('/')
self.clean_namespace = full_namespace[:last_slash_pos] \
if last_slash_pos != 0 else ''

def run(self):
rate = rospy.Rate(1)
while not rospy.is_shutdown():
rate.sleep()
urdf = rospy.get_param(
self.clean_namespace + '/robot_description',
None)
if urdf is not None:
tmp_file = tempfile.mktemp()
with open(tmp_file, "w") as f:
f.write(urdf)
md5sum = checksum_md5(tmp_file)

r = RobotModel()
with open(tmp_file, 'r') as f:
with no_mesh_load_mode():
r.load_urdf_file(f)
robot_name = r.urdf_robot_model.name

rospack = rospkg.RosPack()
kxreus_path = rospack.get_path('kxreus')
eus_path = os.path.join(
kxreus_path, 'models', '{}.l'.format(md5sum))
if os.path.exists(eus_path):
rospy.set_param(self.clean_namespace + '/eus_robot_name',
robot_name)
rospy.set_param(self.clean_namespace + '/eusmodel_hash',
md5sum)
continue

lock_path = eus_path + ".lock"
lock = FileLock(lock_path, timeout=10)
try:
with lock:
with open(eus_path, 'w') as f:
urdf2eus(tmp_file, fp=f)
rospy.loginfo(
'Eusmodel is saved to {}'.format(eus_path))
os.remove(lock_path)
finally:
if os.path.exists(tmp_file):
os.remove(tmp_file)


if __name__ == '__main__':
rospy.init_node('model_server')
server = EusModelServer()
server.run()
81 changes: 81 additions & 0 deletions ros/kxreus/node_scripts/http_server_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env python

from http.server import HTTPServer
from http.server import SimpleHTTPRequestHandler
import os
import threading

from filelock import FileLock
from filelock import Timeout
import rospkg
import rospy


class CustomHTTPRequestHandler(SimpleHTTPRequestHandler):
def __init__(self, *args, directory=None, **kwargs):
self.base_directory = directory
super().__init__(*args, **kwargs)

def translate_path(self, path):
path = super().translate_path(path)
rel_path = os.path.relpath(path, os.getcwd())
full_path = os.path.join(self.base_directory, rel_path) \
if self.base_directory else path
return full_path

def do_GET(self):
filepath = self.translate_path(self.path)
lock_path = filepath + ".lock"

if os.path.exists(lock_path):
try:
with FileLock(lock_path, timeout=1):
super().do_GET()
except Timeout:
self.send_response(503)
self.end_headers()
self.wfile.write(
b"Service Unavailable: File is currently locked.")
else:
super().do_GET()


class ThreadedHTTPServer(object):
def __init__(self, host, port, handler_class, directory):
def handler_factory(*args, **kwargs):
return handler_class(
*args, directory=directory, **kwargs)
self.server = HTTPServer((host, port), handler_factory)
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True

def start(self):
self.server_thread.start()
rospy.loginfo("HTTP Server Running...")

def stop(self):
self.server.shutdown()
self.server.server_close()
rospy.loginfo("HTTP Server Stopped.")


if __name__ == '__main__':
rospy.init_node('http_server_node')

rospack = rospkg.RosPack()
kxreus_path = rospack.get_path('kxreus')
www_directory = os.path.join(kxreus_path, 'models')

full_namespace = rospy.get_namespace()
last_slash_pos = full_namespace.rfind('/')
clean_namespace = full_namespace[:last_slash_pos] \
if last_slash_pos != 0 else ''

port = rospy.get_param(clean_namespace + '/port', 8000)
server = ThreadedHTTPServer(
'0.0.0.0', port, CustomHTTPRequestHandler, www_directory)
server.start()

rospy.spin()

server.stop()
1 change: 1 addition & 0 deletions ros/kxreus/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<!-- ALPHABETICAL ORDER -->

<!-- ALPHABETICAL ORDER -->
<build_depend>pr2eus</build_depend>
<build_depend>roseus</build_depend>
<!-- ALPHABETICAL ORDER -->

Expand Down
4 changes: 2 additions & 2 deletions ros/kxreus/requirements.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scikit-robot==0.0.32
urdfeus==0.0.5
scikit-robot
urdfeus

0 comments on commit 3f88f72

Please sign in to comment.