0. 简介

对于ROS来说,如果不具备一定知识的人员来使用这些我们写的算法,如果说没有交互,这会让用户使用困难,所以我们需要使用GUI来完成友善的数据交互,传统的GUI方法一般有PYQT这类GUI方法,但是这类GUI工具上手门槛较高,也不太适合快速且敏捷的可视化界面开发。NiceGUI 是一个易于使用、基于Python的用户界面框架,它显示在Web浏览器里,可创建按钮,对话框,markdown,3D场景,绘图等。它非常适用于小型网页应用、仪表盘、机器人项目、智能家居解决方案和类似的场景。相关代码也已经全部在Github上开源了。

1. 具体功能

我们打开NICEGUI官网就可以看到NICEGUI支持一些写法了,具体支持

- 交互
  - 按钮、滚动条、输入框
  - 提醒、对话、菜单
  - 键盘输入
  - ...

- 样式设计
  - 自定义颜色主题
  - 自定义CSS
  - 现代设计风格素材
  - 内置Taiwind

- 布局
  - 导航栏、选项卡、面板、...
  - 用行、列和卡片分组
  - HTML 和降价元素
  - 默认Flex

- 代码
  - 实时
  - 代码更改时隐式重新加载
  - 直接的数据绑定
  - 在 Python 内执行 javascript

- 可视化
  - 图表、图表和表格
  - 3D场景
  - 进度条
  - 用于数据刷新的内置定时器
...

2. 环境安装

对于nicegui来说安装很简单,即使用下面指令安装即可

python3 -m pip install nicegui

如果想要使用Docker,也可以使用multi-arch Docker image,在拉下docker后,就可以通过下面的指令运行含有NICEGUI的docker环境了

docker run --rm -p 8888:8080 -v $(pwd):/app/ -it zauberzeug/nicegui:latest

这将使用当前目录中的代码在http://localhost:8888上启动服务器。包含ui.run(port=8080,…)命令的文件必须命名为main.py。代码修改会触发自动重载。

3. 常见api以及在ROS中的使用

NICEGUI中也有良好的API以供读者参考,我们可以在https://nicegui.io/reference中找到NICEGUI常用的API。代码简单易懂,很值得试一下

下面我们根据API可以完成最简单的与ros联合的代码,这里将/status/odometry获取,并通过NICEGUI完成消息的显示整合以及发送

#!/usr/bin/env python3
import rospy
from std_msgs.msg import Empty, String
from geometry_msgs.msg import Twist, Vector3
from nicegui import ui
import json


def send(x, y):
    linear = Vector3(y, 0, 0)
    angular = Vector3(0, 0, -x)
    publish_twist(Twist(linear, angular))


def handle_status(data):
    msg = json.loads(data.data)
    time.text = f'Timestamp: {msg["time"]} ms'


def handle_odometry(data):
    linear.value = data.linear.x
    angular.value = data.angular.z


rospy.init_node('ui')

publish_twist = rospy.Publisher('/steer', Twist, queue_size=1).publish
publish_configure = rospy.Publisher('/configure', Empty, queue_size=1).publish

with ui.row().classes('items-stretch'):
    with ui.card():
        ui.markdown('### Setup')
        ui.button('Configure', on_click=lambda: publish_configure())
    with ui.card() as status:
        ui.markdown('### Status')
        time = ui.label()
    with ui.card():
        ui.markdown('### Control')
        ui.joystick(
            color='blue',
            size=50,
            on_move=lambda msg: send(msg.data.vector.x, msg.data.vector.y),
            on_end=lambda _: send(0, 0),
        )
    with ui.card() as odometry:
        ui.markdown('### Odometry')
        ui.label('linear')
        linear = ui.slider(min=-1.0, max=1.0, step=0.01).props('label-always readonly')
        ui.label('angular')
        angular = ui.slider(min=-1.0, max=1.0, step=0.01).props('label-always readonly')

    ui.timer(0.1, lambda: None)  # NOTE: update ui

rospy.Subscriber('/status', String, handle_status, queue_size=1)
rospy.Subscriber('/odometry', Twist, handle_odometry, queue_size=1)

ui.run(title='ROS Example', reload=False, show=False)

4. 参考链接

https://nicegui.io/reference

https://mp.weixin.qq.com/s/QHnA9s9swwvtiC0PK9Fzsg

https://zhuanlan.zhihu.com/p/600812693