目标
实现带有表格和图表的Scaler与电机步进扫描。
在本课程中,我们将展示如何利用Bluesky提供的工具,在数据采集过程中同时以表格形式和图形视图展示数据。这些功能通过使用回调函数实现。在课程1和2中,我们编写了简单的自定义回调函数来查看RunEngine执行计划时生成的文档。然而,数据很快变得过于复杂,难以通过简单方式查看。
LiveTable和LivePlot回调函数分别提供了计划数据的表格视图和图形视图。我们将首先介绍它们。之后,我们将展示BestEffortCallback,它整合了上述两种回调函数并额外提供了一些功能。在日常工作中,我们会希望一直使用BestEffortCallback。我们将演示如何设置它以实现"一次设置,永久使用"。
从基本的scaler和motor配置开始,我们首先组合import一起, 因为这是常见的Python操作:
python
from bluesky import RunEngine
from ophyd import EpicsMotor
from ophyd.scaler import ScalerCH
from bluesky import RunEngine
import bluesky.plans as bp
from bluesky.callbacks import LiveTable
from bluesky.callbacks import LivePlot
from bluesky.callbacks.best_effort import BestEffortCallback
from apstools.devices import use_EPICS_scaler_channels
接着, 创建一个(用于扫描的)RunEngine并且连接电机和scaler.
python
RE = RunEngine({})
m1 = EpicsMotor("MotorVM:m1", name="m1")
scaler = ScalerCH("NCT16:scaler1", name="scaler")
m1.wait_for_connection()
scaler.wait_for_connection()
重新配置scaler通道名称,设置计数时间0.5秒, 读取scaler的值:
python
scaler.channels.chan01.chname.put("clock")
scaler.channels.chan02.chname.put("I0")
scaler.channels.chan03.chname.put("scint")
scaler.select_channels()
scaler.preset_time.put(0.5)
print(scaler.read())

展示数据
在前两个课程中,我们编写了一个回调例程,用于在扫描过程中打印信息(即打印运行引擎执行计划时发出的文档流中的选定内容)。但我们的回调较为简单,我们发现运行引擎输出的文档包含大量内容。
Bluesky中最简单的回调函数示例是print函数。然而,我们需要的是一个能够理解数据、并能基于合理推断在数据采集过程中动态展示数据的回调函数。
显示数据的一种方法是使用随扫描进度更新的表格。我们将从Bluesky库中导入LiveTable回调函数:
python
from bluesky.callbacks import LiveTable
LiveTable()在执行计划中展示采集的数据. 参数是要在表格中展示的探测器列表. 首先, 我们对scaler计数5次.
python
RE(bp.count([scaler], num=5), LiveTable([scaler]))

您会看到数据采集序列号、采集时间以及各个已命名的Scaler通道对应的列。最后,还会显示扫描的uid短格式以及扫描编号(scan num),后者是更便于引用的扫描标识。用户可以控制设置或重置扫描编号,因此请勿依赖该编号的唯一性。
接下来,我们将按照基础电机课程中的方式,使用电机和定标器进行扫描,并在LiveTable中显示采集的数据。
python
RE(bp.scan([scaler], m1, 1, 5, 5), LiveTable([m1, scaler]))

除了上方计数表格中的数据列外,还会显示电机位置信息(分别对应电机实际报告的位置和指令要求到达的目标位置)。
此外,还有一款回调函数能够在数据采集过程中实时绘制图表。在启动图形功能前,需要先初始化显示器的图形管理器。该设置步骤与具体使用的图形管理器类型密切相关。
对于jupyter notebook:
python
%matplotlib inline
我们将从Bluesky库导入LivePlot回调:
python
from bluesky.callbacks import LivePlot
对scaler计数5次, 我们只绘制scint信号.
python
RE(bp.count([scaler],num=5), LivePlot("scint"))

要扫描, 我们需要告诉LivePlot绘制scint vs 电机:
python
RE(bp.scan([scaler], m1, 1,5,5), LivePlot("scint","m1"))

表格与图表均是日常使用中极具价值的诊断工具。它们已被整合至Best-Efforts Callback中,该回调可为任何计划提供可视化支持。它利用用户可配置的信息(这些信息是每个ophyd设备的一部分),来合理推断在当前计划背景下应显示哪些信息是恰当的。
我们从Bluesky库导入BestEffortCallback回调:
python
from bluesky.callbacks.best_effort import BestEffortCallback
bec = BestEffortCallback()
计数scaler 5次:
python
RE(bp.count([scaler], num=5), bec)

在Jupyter notebook中,您可能会看到LiveTable和LivePlot的输出在此处混在一起显示。它们都是按需创建,并随着计划执行进度而更新的。若在命令行环境下执行,LivePlot会显示在独立的窗口中。
重复执行相同的扫描,值得注意的是,我们无需告知回调函数需要显示哪些内容:
python
RE(bp.scan([scaler],m1, 1,5,5), bec)

鉴于这是一个非常有用的工具,我们希望使该回调函数始终生效。运行引擎管理着一个此类回调函数的列表。我们将创建一个新的运行引擎,并将BestEffortCallback订阅到其中:
python
RE = RunEngine({})
RE.subscribe(bec)
重复执行scaler的计数操作(无需在命令中额外添加回调函数):
在Jupyter notebook中,我们可以在扫描命令后看到LiveTable。要查看LivePlot,则需要向上翻看几个单元格,其中展示了Scaler通道相对于m1的图表,我们最新的数据通过图例中的扫描编号(scan num)进行标识。
python
RE(bp.count([scaler], num=5))

在Jupyter notebook中,我们可以在计数命令后直接看到LiveTable。而要查看LivePlot,则需要向上翻看几个单元格,那里会显示定标器通道随时间变化的图表,我们最新的数据通过图例中的扫描编号进行标识。
然后,重复执行扫描(同样,无需在命令中添加回调函数):
python
RE(bp.scan([scaler], m1, 1, 5, 5))


以下用ophyd.sim自带的模拟硬件进行扫描:
python
from ophyd.sim import det as scaler
from ophyd.sim import motor
RE(bp.scan([scaler], motor, -5, 5, 35))
扫描结果:
