1. 为什么要用 omni.ui
在 Isaac Sim 中做机器人、传感器或控制算法开发时,除了看 3D 视口,我们通常还需要一些额外的界面来辅助调试,比如:
- 显示当前状态值
- 动态观察一段时间内的数值变化
- 提供按钮、开关、输入框来控制实验
- 用结构化布局把调试信息整理到一个窗口里
这类界面最常用的接口之一就是:
1 | import omni.ui as ui |
omni.ui 是 Omniverse / Isaac Sim 提供的原生 UI 工具库。它可以用来快速创建窗口、布局、文本、按钮、图表等控件,适合做轻量级工具面板、调试面板和交互界面。
下图是我在 Isaac Sim 仿真时使用这个接口做出来的效果:

2. omni.ui 能做什么
它的功能可以简单理解为两大类。
2.1 布局和显示
它可以创建各种基本界面元素,例如:
ui.Window:窗口ui.Label:文本标签ui.Button:按钮ui.StringField:文本输入框ui.IntField/ui.FloatField:数值输入ui.CheckBox:复选框ui.ComboBox:下拉框ui.Plot:曲线图
2.2 容器和排版
它也提供了常用布局容器:
ui.VStack:垂直排列ui.HStack:水平排列ui.ZStack:叠加排列ui.ScrollingFrame:带滚动条的区域ui.CollapsableFrame:可折叠分组
所以我们通常会把 omni.ui 用在这些场景里:
- 实时状态监控窗口
- 仿真参数调节面板
- 机器人调试面板
- 传感器数据显示面板
- 小型交互工具
3. 最基本的使用方式
3.1 创建一个窗口
最简单的例子如下:
1 | import omni.ui as ui |
这里有两个关键点:
ui.Window(...)用来创建窗口with window.frame:表示后面的控件都放进这个窗口里
运行后,会出现一个标题为 My Window 的窗口,里面只有一行文本。
4. with 结构为什么这么常见
omni.ui 的代码风格很像声明式布局,常见写法是:
1 | with some_container: |
这里的意思不是“执行某个逻辑块”,而是“在这个容器里添加控件”。
比如:
1 | window = ui.Window("Demo", width=400, height=200) |
表示:
- 在窗口里放一个垂直布局
- 在这个垂直布局里放两行标签
5. 常见布局控件
布局是 omni.ui 里最重要的部分之一,因为大部分界面都是靠布局容器拼出来的。
5.1 ui.VStack
垂直堆叠布局:
1 | with ui.VStack(spacing=6): |
效果就是 A、B、C 从上到下排列。
常见参数:
spacing:控件之间的间距height/width:大小控制
5.2 ui.HStack
水平排列布局:
1 | with ui.HStack(spacing=8): |
效果是标签和输入框左右排开。
5.3 ui.ZStack
叠层布局,适合把多个元素画在同一个区域里,比如背景和前景叠加。
5.4 ui.ScrollingFrame
如果内容很多,可以放到滚动区域里:
1 | with ui.ScrollingFrame(): |
这样内容超出窗口时会出现滚动条。
6. 最常用的基础控件
6.1 ui.Label
显示文本:
1 | ui.Label("Current status: running") |
也可以指定宽高:
1 | ui.Label("speed", width=120, height=20) |
6.2 ui.Button
按钮通常带一个回调函数:
1 | def on_click(): |
当按钮被点击时,会执行 on_click()。
6.3 ui.CheckBox
适合布尔开关:
1 | checkbox_model = ui.SimpleBoolModel(False) |
一般会配合 model 一起用,因为 UI 控件的状态通常是通过 model 读写的。
6.4 输入框
omni.ui 中很多输入控件都依赖 model。
例如字符串输入:
1 | string_model = ui.SimpleStringModel("hello") |
浮点输入:
1 | float_model = ui.SimpleFloatModel(0.5) |
后续可以通过:
1 | value = float_model.get_value_as_float() |
读取当前值。
7. model 是什么
这是 omni.ui 初学者最容易卡住的地方。
很多控件并不是直接把值存进控件本身,而是通过一个“数据模型”来管理状态。常见 model 有:
ui.SimpleStringModelui.SimpleIntModelui.SimpleFloatModelui.SimpleBoolModel
例如:
1 | model = ui.SimpleFloatModel(1.23) |
这时:
field是显示控件model才是真正存数值的地方
读取值:
1 | current = model.get_value_as_float() |
设置值:
1 | model.set_value(2.5) |
这种设计的好处是:
- 逻辑和显示分离
- 多个控件可以共享同一个状态
- 更适合响应式更新
8. 如何做一个简单的控制面板
下面是一个典型的参数面板:
1 | import omni.ui as ui |
这个例子展示了几个关键模式:
- 用 model 保存控件状态
- 用
HStack做“标签 + 输入框”的一行 - 用
Button触发回调 - 在回调里读取 UI 当前值
9. 如何做一个实时数据显示面板
除了输入控制,omni.ui 也非常适合做状态监视器。
9.1 最简单的数值显示
1 | import omni.ui as ui |
只要在仿真循环里调用:
1 | panel.update(pos, vel) |
窗口里的文本就会刷新。
10. 如何使用 ui.Plot 画实时曲线
这是调试里非常常用的一种能力。
10.1 最小示例
1 | import omni.ui as ui |
10.2 ui.Plot 的核心参数
1 | ui.Plot( |
含义如下:
ui.Type.LINE:折线图scale_min/scale_max:y 轴上下界*data:数据点序列width/height:控件尺寸
10.3 刷新图像
曲线不会自动更新,必须主动调用:
1 | self.plot.set_data(*self.data) |
这一步是实时绘图最关键的地方。
11. 多条曲线怎么组织
如果需要显示多路数据,最常见的方法是“每条数据一行”。
例如:
1 | with ui.VStack(spacing=6): |
也可以进一步包装成一个类,统一管理:
- 曲线名称
- 历史数据
- 当前值标签
plot控件对象
这样后续新增观测量会更方便。
12. 如何做一个带滚动条的监视器
当观测量很多时,可以把内容放到 ScrollingFrame 中:
1 | self.window = ui.Window("Monitor", width=460, height=500) |
这样即使信号很多,也不会把窗口撑爆。
13. 控件大小和布局比例怎么控制
常用方法有这些。
13.1 固定宽度
1 | ui.Label("Name", width=120) |
13.2 使用 ui.Fraction
让控件占据剩余空间的一部分:
1 | ui.Plot(..., width=ui.Fraction(1)) |
这个很适合让图表自动铺满整行。
13.3 设置高度
1 | ui.Plot(..., height=90) |
对于曲线图尤其常见。
14. omni.ui 常见编程模式
在实际项目里,最常见的是下面这几种组织方式。
14.1 方式一:直接写在一个函数里
适合一次性小工具:
1 | def build_ui(): |
优点是简单,缺点是后续不好维护。
14.2 方式二:封装成类
适合中等复杂度的工具面板:
1 | class DemoPanel: |
优点是结构清晰,状态和回调更容易管理。
14.3 方式三:UI 和逻辑分离
如果项目更复杂,可以把:
- UI 构建
- 数据模型
- 仿真逻辑
拆开管理。
这也是更推荐的工程化做法。
15. 一个完整的小例子:实时调试窗口
下面给出一个稍完整的例子,展示窗口、布局、标签、按钮和实时曲线的配合方式。
1 | import math |
这个例子里包含了:
- 一个窗口
- 一条实时曲线
- 当前值显示
- 两个按钮
- 数据清零和打印逻辑
它已经足够作为很多调试工具的起点。
16. 使用 omni.ui 时的常见注意点
16.1 不要忘记保存控件引用
如果后续还要更新某个控件,比如:
Label.textPlot.set_data(...)
那就必须把控件保存成成员变量,例如:
1 | self.value_label = ui.Label("0.0") |
否则后面就拿不到它。
16.2 图表不会自动刷新
更新数据后,需要显式调用:
1 | self.plot.set_data(*data) |
16.3 数值输入通常要配合 model
输入控件不是直接返回值,通常通过 model 管理。
16.4 界面复杂后建议封装
如果窗口里控件很多,不建议全部堆在一个函数里,最好封装成类。
16.5 实时图表要限制历史长度
如果每一帧都追加数据而不裁剪,列表会越来越长,影响性能。
更常见的写法是只保留固定窗口长度:
1 | if len(data) > history_len: |
17. omni.ui 适合什么,不适合什么
17.1 很适合
- 调试面板
- 参数面板
- 实时状态窗口
- 小型交互工具
- 实验辅助界面
17.2 不太适合
- 特别复杂的大型前端界面
- 高级交互动画页面
- 复杂图表系统
omni.ui 的定位更偏轻量、原生、快速集成,而不是一个完整的 Web 前端框架。
18. 学习顺序建议
如果第一次接触 omni.ui,推荐按这个顺序掌握:
ui.Windowui.VStack/ui.HStackui.Labelui.Buttonmodel机制ui.Plot- 封装成类
这样会比较自然。
19. 总结
import omni.ui as ui 是 Isaac Sim 中构建原生调试界面的基础接口。
它最核心的价值在于:
- 能快速创建界面
- 能和仿真代码直接集成
- 能把“看 3D 画面”升级成“看结构化的状态与趋势”
在实际使用中,你通常只需要先掌握这几件事:
ui.Window创建窗口ui.VStack/ui.HStack组织布局ui.Label和ui.Button做基础交互ui.SimpleXxxModel管理输入状态ui.Plot显示实时曲线
掌握这些之后,就已经足够写出大多数调试面板。
如果后续要继续深入,最值得扩展的方向通常是:
- 更复杂的布局组织
- model 驱动的参数系统
- 多曲线监视器
- 按钮、输入和仿真控制逻辑联动
一句话概括:
omni.ui 不是为了做漂亮网页,而是为了让你在 Isaac Sim 里快速拥有一个“能看、能改、能调”的原生工具面板。