This commit is contained in:
Nils Schulte 2024-01-20 20:58:47 +01:00
commit 6b61f3e945
4 changed files with 262 additions and 0 deletions

26
package.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<package format="2">
<name>twist_web</name>
<version>1.0.0</version>
<description>
A robot-agnostic teleoperation node in the browser to output Twist
messages.
</description>
<maintainer email="git@nilsschulte.de">Nils Schulte</maintainer>
<license>BSD License 2.0</license>
<autor email="git@nilsschulte.de">Nils Schulte</autor>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>rclpy</exec_depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>

4
setup.cfg Normal file
View File

@ -0,0 +1,4 @@
[develop]
script_dir=$base/lib/twist_web
[install]
install_scripts=$base/lib/twist_web

38
setup.py Normal file
View File

@ -0,0 +1,38 @@
from setuptools import setup
package_name = 'twist_web'
setup(
name=package_name,
version='2.3.2',
packages=[],
py_modules=[
'twist_web'
],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Chris Lalancette',
maintainer_email='clalancette@openrobotics.org',
author='Graylin Trevor Jay, Austin Hendrix',
keywords=['ROS'],
classifiers=[
'Intended Audience :: Developers',
'License :: BSD',
'Programming Language :: Python',
'Topic :: Software Development',
],
description='A robot-agnostic teleoperation node to convert keyboard'
'commands to Twist messages.',
license='BSD',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'twist_web = twist_web:main'
],
},
)

194
twist_web.py Executable file
View File

@ -0,0 +1,194 @@
# Copyright 2011 Brown University Robotics.
# Copyright 2017 Open Source Robotics Foundation, Inc.
# All rights reserved.
#
# Software License Agreement (BSD License 2.0)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of the Willow Garage nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import sys
import threading
import geometry_msgs.msg
import rclpy
page= """
<!DOCTYPE HTML>
<html>
<script src="http://bobboteck.github.io/joy/joy.js">
</script>
<script type="text/javascript">
// Create JoyStick object into the DIV 'joyDiv'
function httpGetAsync(theUrl, callback)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
window.addEventListener("load", () => {
joy = new JoyStick('joyDiv');
setInterval(function(){
joy.GetX();
httpGetAsync("/update?x="+joy.GetX()+"&y="+joy.GetY(), () => {});
}, 200);});
</script>
<body>
<div id="joyDiv" style="width:500px;height:500px;margin-bottom:20px;"></div>
</body>
</html>
"""
moveBindings = {
}
speedBindings = {
}
from flask import Flask, request, redirect
app = Flask(__name__)
rclpy.init()
node = rclpy.create_node('twist_web')
@app.route("/")
def hello_world():
return page
@app.route('/update', methods=['GET'])
def handle_get():
global twist_msg, twist, pub
if request.method == 'GET':
pos = (int(request.args['y']),int(request.args['x']))
print(pos)
if stamped:
twist_msg.header.stamp = node.get_clock().now().to_msg()
twist.linear.x = pos[0] / 500
twist.linear.y = 0.0
twist.linear.z = 0.0
twist.angular.x = 0.0
twist.angular.y = 0.0
twist.angular.z = -pos[1] /100
pub.publish(twist_msg)
return "ok"
def main():
global node, pub, spinner, TwistMsg, stamped, frame_id, twist, twist_msg
# parameters
stamped = node.declare_parameter('stamped', True).value
frame_id = node.declare_parameter('frame_id', '').value
if not stamped and frame_id:
raise Exception("'frame_id' can only be set when 'stamped' is True")
if stamped:
TwistMsg = geometry_msgs.msg.TwistStamped
else:
TwistMsg = geometry_msgs.msg.Twist
twist_msg = TwistMsg()
if stamped:
twist = twist_msg.twist
twist_msg.header.stamp = node.get_clock().now().to_msg()
twist_msg.header.frame_id = frame_id
else:
twist = twist_msg
pub = node.create_publisher(TwistMsg, 'staubi_base_controller/cmd_vel', 10)
speed = 0.5
turn = 1.0
x = 0.0
y = 0.0
z = 0.0
th = 0.0
status = 0.0
if stamped:
twist = twist_msg.twist
twist_msg.header.stamp = node.get_clock().now().to_msg()
twist_msg.header.frame_id = frame_id
else:
twist = twist_msg
try:
return
print(msg)
print(vels(speed, turn))
while True:
key = getKey(settings)
if key in moveBindings.keys():
x = moveBindings[key][0]
y = moveBindings[key][1]
z = moveBindings[key][2]
th = moveBindings[key][3]
elif key in speedBindings.keys():
speed = speed * speedBindings[key][0]
turn = turn * speedBindings[key][1]
print(vels(speed, turn))
if (status == 14):
print(msg)
status = (status + 1) % 15
else:
x = 0.0
y = 0.0
z = 0.0
th = 0.0
if (key == '\x03'):
break
if stamped:
twist_msg.header.stamp = node.get_clock().now().to_msg()
twist.linear.x = x * speed
twist.linear.y = y * speed
twist.linear.z = z * speed
twist.angular.x = 0.0
twist.angular.y = 0.0
twist.angular.z = th * turn
pub.publish(twist_msg)
except Exception as e:
print(e)
if __name__ == '__main__':
main()
spinner = threading.Thread(target=rclpy.spin, args=(node,))
spinner.start()
app.run(host='0.0.0.0')