Falko Schindler преди 1 година
родител
ревизия
cca1286d74

+ 1 - 3
examples/ros2/Dockerfile

@@ -8,8 +8,7 @@ RUN apt-get update && apt-get install -y \
 
 SHELL ["/bin/bash", "-c"]
 
-COPY requirements.txt requirements.txt
-RUN pip3 install -r requirements.txt
+RUN pip3 install nicegui
 
 ADD ros2_ws /ros2_ws
 
@@ -20,7 +19,6 @@ RUN source /opt/ros/humble/setup.bash && \
 
 COPY ros_entrypoint.sh /
 
-
 EXPOSE 8080
 
 ENTRYPOINT ["/ros_entrypoint.sh"]

+ 1 - 2
examples/ros2/README.md

@@ -5,7 +5,7 @@ It starts up a user interface with virtual joystick and pose visualization reach
 The joystick publishes twist messages which are consumed by a simple simulator node which itself publishes the new pose of a virtual mobile robot.
 ROS2 and NiceGUI are fully functional in this example.
 
-Over all it's a bit more complex than a super minimal example to allow auto-reloading of the NiceGUI ROS2 Node.
+Over all it is a bit more complex than a super minimal example to allow auto-reloading of the NiceGUI ROS2 node.
 
 ## Usage
 
@@ -18,4 +18,3 @@ docker compose up --build
 Afterwards you can access the user interface at http://127.0.0.1:8080.
 
 If you want to run the NiceGUI node locally or in your own own ROS2 project, you can simply copy the code from the `ros2_ws/src/gui` folder.
-The code is provided with no warranties under the MIT License.

+ 0 - 2
examples/ros2/requirements.txt

@@ -1,2 +0,0 @@
-nicegui
-pyquaternion

+ 12 - 19
examples/ros2/ros2_ws/src/gui/gui/node.py

@@ -1,11 +1,9 @@
+import math
 import threading
-from dataclasses import dataclass
 from pathlib import Path
-from typing import List
 
 import rclpy
 from geometry_msgs.msg import Pose, Twist
-from pyquaternion import Quaternion
 from rclpy.executors import ExternalShutdownException
 from rclpy.node import Node
 
@@ -13,25 +11,24 @@ from nicegui import app, globals, run, ui
 
 
 class NiceGuiNode(Node):
+
     def __init__(self) -> None:
         super().__init__('nicegui')
         self.cmd_vel_publisher = self.create_publisher(Twist, 'cmd_vel', 1)
-        self.subscription = self.create_subscription(Pose, 'pose', self.on_pose, 1)
+        self.subscription = self.create_subscription(Pose, 'pose', self.handle_pose, 1)
 
         with globals.index_client:
             with ui.row().classes('items-stretch'):
                 with ui.card().classes('w-44 text-center items-center'):
                     ui.label('Control').classes('text-2xl')
-                    ui.joystick(
-                        color='blue', size=50,
-                        on_move=lambda e: self.send_speed(float(e.y), float(e.x)),
-                        on_end=lambda _: self.send_speed(0.0, 0.0),
-                    )
+                    ui.joystick(color='blue', size=50,
+                                on_move=lambda e: self.send_speed(e.y, e.x),
+                                on_end=lambda _: self.send_speed(0.0, 0.0))
                     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'):
                     ui.label('Data').classes('text-2xl')
                     ui.label('linear velocity').classes('text-xs mb-[-1.8em]')
-                    slider_props = 'readonly selection-color="transparent"'
+                    slider_props = 'readonly selection-color=transparent'
                     self.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]')
                     self.angular = ui.slider(min=-1, max=1, step=0.05, value=0).props(slider_props)
@@ -41,12 +38,10 @@ class NiceGuiNode(Node):
                     ui.label('Visualization').classes('text-2xl')
                     with ui.scene(350, 300) as scene:
                         with scene.group() as self.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)
+                            prism = [[-0.5, -0.5], [0.5, -0.5], [0.75, 0], [0.5, 0.5], [-0.5, 0.5]]
+                            self.robot_object = scene.extrusion(prism, 0.4).material('#4488ff', 0.5)
 
-    def send_speed(self, x, y) -> None:
+    def send_speed(self, x: float, y: float) -> None:
         msg = Twist()
         msg.linear.x = x
         msg.angular.z = -y
@@ -54,12 +49,10 @@ class NiceGuiNode(Node):
         self.angular.value = y
         self.cmd_vel_publisher.publish(msg)
 
-    def on_pose(self, msg: Pose) -> None:
+    def handle_pose(self, msg: Pose) -> None:
         self.position.text = f'x: {msg.position.x:.2f}, y: {msg.position.y:.2f}'
-        self.position.update()
         self.robot_3d.move(msg.position.x, msg.position.y)
-        quaternion = Quaternion(msg.orientation.w, msg.orientation.x, msg.orientation.y, msg.orientation.z)
-        self.robot_3d.rotate(0, 0, quaternion.yaw_pitch_roll[0])
+        self.robot_3d.rotate(0, 0, 2 * math.atan2(msg.orientation.z, msg.orientation.w))
 
 
 def node_spin(node: Node) -> None:

+ 1 - 1
examples/ros2/ros2_ws/src/gui/package.xml

@@ -3,7 +3,7 @@
 <package format="3">
   <name>gui</name>
   <version>1.0.0</version>
-  <description>This is an example of NiceGUI in a ROS2 node based that uses a joystick to control turtlesim.</description>
+  <description>This is an example of NiceGUI in a ROS2 node that uses a joystick to send geometry_msgs/Twist messages.</description>
   <maintainer email="nicegui@zauberzeug.com">Zauberzeug GmbH</maintainer>
   <license>MIT License</license>
   

+ 1 - 1
examples/ros2/ros2_ws/src/simulator/package.xml

@@ -3,7 +3,7 @@
 <package format="3">
   <name>simulator</name>
   <version>1.0.0</version>
-  <description>A very simple simulator which receives geometry_msgs/Twist messages and publishes geometry_msgs/Pose messages on the 'pose' topic.</description>
+  <description>A very simple simulator which receives geometry_msgs/Twist messages and publishes geometry_msgs/Pose messages on the "pose" topic.</description>
   <maintainer email="nicegui@zauberzeug.com">Zauberzeug GmbH</maintainer>
   <license>MIT License</license>
   

+ 13 - 19
examples/ros2/ros2_ws/src/simulator/simulator/node.py

@@ -2,42 +2,36 @@ import math
 
 import rclpy
 from geometry_msgs.msg import Pose, Twist
-from pyquaternion import Quaternion
 from rclpy.node import Node
 
 
 class Simulator(Node):
+    INTERVAL = 0.1
 
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__('simulator')
         self.pose_publisher_ = self.create_publisher(Pose, 'pose', 1)
-        self.subscription = self.create_subscription(Twist, 'cmd_vel', self.on_steering, 1)
+        self.subscription = self.create_subscription(Twist, 'cmd_vel', self.handle_velocity_command, 1)
         self.pose = Pose()
         self.linear_velocity = 0.0
         self.angular_velocity = 0.0
-        self.step_size = 0.1
-        self.timer = self.create_timer(self.step_size, self.update_pose)
+        self.timer = self.create_timer(self.INTERVAL, self.update_pose)
 
-    def on_steering(self, msg):
+    def handle_velocity_command(self, msg: Twist) -> None:
         self.linear_velocity = msg.linear.x
         self.angular_velocity = msg.angular.z
 
-    def update_pose(self):
-        quaternion = Quaternion(self.pose.orientation.w, self.pose.orientation.x,
-                                self.pose.orientation.y, self.pose.orientation.z)
-        theta = quaternion.yaw_pitch_roll[0]
-        self.pose.position.x += self.linear_velocity * math.cos(theta) * self.step_size
-        self.pose.position.y += self.linear_velocity * math.sin(theta) * self.step_size
-        theta += self.angular_velocity * self.step_size
-        quaternion = Quaternion(axis=[0.0, 0.0, 1.0], radians=theta)
-        self.pose.orientation.x = quaternion.x
-        self.pose.orientation.y = quaternion.y
-        self.pose.orientation.z = quaternion.z
-        self.pose.orientation.w = quaternion.w
+    def update_pose(self) -> None:
+        yaw = 2 * math.atan2(self.pose.orientation.z, self.pose.orientation.w)
+        self.pose.position.x += self.linear_velocity * math.cos(yaw) * self.INTERVAL
+        self.pose.position.y += self.linear_velocity * math.sin(yaw) * self.INTERVAL
+        yaw += self.angular_velocity * self.INTERVAL
+        self.pose.orientation.z = math.sin(yaw / 2)
+        self.pose.orientation.w = math.cos(yaw / 2)
         self.pose_publisher_.publish(self.pose)
 
 
-def main(args=None):
+def main(args=None) -> None:
     rclpy.init(args=args)
     simulator = Simulator()
     rclpy.spin(simulator)

+ 1 - 1
setup.py

@@ -7,5 +7,5 @@ setup(
     author='Zauberzeug',
     author_email='info@zauberzeug.com',
     url='https://github.com/zauberzeug/nicegui/',
-    packages=['gui'],
+    packages=['nicegui'],
 )