|
@@ -4,6 +4,7 @@ from pathlib import Path
|
|
|
|
|
|
import rclpy
|
|
import rclpy
|
|
from geometry_msgs.msg import Pose, Twist
|
|
from geometry_msgs.msg import Pose, Twist
|
|
|
|
+from pyquaternion import Quaternion
|
|
from rclpy.executors import ExternalShutdownException
|
|
from rclpy.executors import ExternalShutdownException
|
|
from rclpy.node import Node
|
|
from rclpy.node import Node
|
|
|
|
|
|
@@ -15,7 +16,7 @@ class User():
|
|
linear: ui.slider = None
|
|
linear: ui.slider = None
|
|
angular: ui.slider = None
|
|
angular: ui.slider = None
|
|
position: ui.label = None
|
|
position: ui.label = None
|
|
- map: ui.html = None
|
|
|
|
|
|
+ robot_3d: ui.scene.group = None
|
|
|
|
|
|
|
|
|
|
class NiceGuiNode(Node):
|
|
class NiceGuiNode(Node):
|
|
@@ -36,7 +37,7 @@ class NiceGuiNode(Node):
|
|
on_move=lambda e: self.send_speed(float(e.y), float(e.x)),
|
|
on_move=lambda e: self.send_speed(float(e.y), float(e.x)),
|
|
on_end=lambda _: self.send_speed(0.0, 0.0),
|
|
on_end=lambda _: self.send_speed(0.0, 0.0),
|
|
)
|
|
)
|
|
- ui.label('Publish steering commands by dragging your mouse around in the blue field')
|
|
|
|
|
|
+ ui.label('Publish steering commands by dragging your mouse around in the blue field').classes('mt-6')
|
|
with ui.card().classes('w-44 text-center items-center'):
|
|
with ui.card().classes('w-44 text-center items-center'):
|
|
ui.label('Data').classes('text-2xl')
|
|
ui.label('Data').classes('text-2xl')
|
|
ui.label('linear velocity').classes('text-xs mb-[-1.8em]')
|
|
ui.label('linear velocity').classes('text-xs mb-[-1.8em]')
|
|
@@ -44,12 +45,18 @@ class NiceGuiNode(Node):
|
|
user.linear = ui.slider(min=-1, max=1, step=0.05, value=0).props(slider_props)
|
|
user.linear = ui.slider(min=-1, max=1, step=0.05, value=0).props(slider_props)
|
|
ui.label('angular velocity').classes('text-xs mb-[-1.8em]')
|
|
ui.label('angular velocity').classes('text-xs mb-[-1.8em]')
|
|
user.angular = ui.slider(min=-1, max=1, step=0.05, value=0).props(slider_props)
|
|
user.angular = ui.slider(min=-1, max=1, step=0.05, value=0).props(slider_props)
|
|
- ui.label('position').classes('text-xs mt-8 mb-[-1.4em]')
|
|
|
|
- user.position = ui.label('x: 0.0, y: 0.0')
|
|
|
|
|
|
+ ui.label('position').classes('text-xs mb-[-1.4em]')
|
|
|
|
+ user.position = ui.label('---')
|
|
with ui.card().classes('w-96 h-96 items-center'):
|
|
with ui.card().classes('w-96 h-96 items-center'):
|
|
ui.label('Visualization').classes('text-2xl')
|
|
ui.label('Visualization').classes('text-2xl')
|
|
- user.map = ui.html()
|
|
|
|
|
|
+ with ui.scene(350, 300) as scene:
|
|
|
|
+ with scene.group() as user.robot_3d:
|
|
|
|
+ prism = [(-0.5, -0.5), (0.5, -0.5), (0.75, 0), (0.5, 0.5), (-0.5, 0.5)]
|
|
|
|
+ outline = list(map(list, prism))
|
|
|
|
+ self.robot_object = scene.extrusion(outline, 0.4)
|
|
|
|
+ self.robot_object.material('#4488ff', 0.5)
|
|
self.users.append(user)
|
|
self.users.append(user)
|
|
|
|
+ app.on_disconnect(lambda: self.users.remove(user))
|
|
|
|
|
|
def send_speed(self, x, y) -> None:
|
|
def send_speed(self, x, y) -> None:
|
|
msg = Twist()
|
|
msg = Twist()
|
|
@@ -63,18 +70,18 @@ class NiceGuiNode(Node):
|
|
def on_pose(self, msg: Pose) -> None:
|
|
def on_pose(self, msg: Pose) -> None:
|
|
for user in self.users:
|
|
for user in self.users:
|
|
user.position.text = f'x: {msg.position.x:.2f}, y: {msg.position.y:.2f}'
|
|
user.position.text = f'x: {msg.position.x:.2f}, y: {msg.position.y:.2f}'
|
|
- user.map.content = f'''
|
|
|
|
- <svg width="100%" height="100%" viewBox="-20 -20 40 40">
|
|
|
|
- <circle cx="{msg.position.x}" cy="{msg.position.y}" r="1" fill="red" />
|
|
|
|
- </svg>'''
|
|
|
|
|
|
+ user.position.update()
|
|
|
|
+ user.robot_3d.move(msg.position.x, msg.position.y)
|
|
|
|
+ quaternion = Quaternion(msg.orientation.w, msg.orientation.x,
|
|
|
|
+ msg.orientation.y, msg.orientation.z)
|
|
|
|
+ user.robot_3d.rotate(0, 0, quaternion.yaw_pitch_roll[0])
|
|
|
|
|
|
|
|
|
|
def node_spin(node: Node) -> None:
|
|
def node_spin(node: Node) -> None:
|
|
try:
|
|
try:
|
|
- print(f'starting node', flush=True)
|
|
|
|
rclpy.spin(node)
|
|
rclpy.spin(node)
|
|
except ExternalShutdownException:
|
|
except ExternalShutdownException:
|
|
- print(f'stopped node', flush=True)
|
|
|
|
|
|
+ pass
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
def main() -> None:
|
|
@@ -89,4 +96,4 @@ def ros_main() -> None:
|
|
|
|
|
|
app.on_startup(lambda: threading.Thread(target=ros_main).start())
|
|
app.on_startup(lambda: threading.Thread(target=ros_main).start())
|
|
run.APP_IMPORT_STRING = f'{__name__}:app'
|
|
run.APP_IMPORT_STRING = f'{__name__}:app'
|
|
-ui.run(uvicorn_reload_dirs=str(Path(__file__).parent.resolve()))
|
|
|
|
|
|
+ui.run(uvicorn_reload_dirs=str(Path(__file__).parent.resolve()), favicon='🤖')
|