Python实现的实时操作

18

我是一名经验不足的Python编程者,我的需求可能比较复杂。我是一名认知科学家,需要精确的刺激呈现和按钮按下检测。有人告诉我最好的方法是使用实时操作系统,但我不知道如何去做。理想情况下,每个试验都应该在实时模式下运行,然后一旦试验结束,操作系统就可以回到不那么细致的状态。大约会有56个试验。是否有一种方法可以从我的Python脚本中编写这个程序呢?

(另一方面,我只需要知道刺激实际上何时被显示。实时方法可以确保刺激在我想要它呈现时被显示,这是一种自上而下的方法。另一方面,如果仅仅知道计算机何时真正有机会将其显示出来更容易,我可以采用更底层的方法。)


1
在今天的计算机上,显示图像并响应按钮点击非常快速,即使是“普通”的GUI应用程序也是如此。我认为人类反应时间比GUI事件反应时间大得多。您确定需要额外的精度吗? - Ferdinand Beyer
是的,我确定。我期望看到的效果在45毫秒左右。当然,每一件小事都足够“快”,但足够的“快”可能会编译成一个效果或掩盖一个效果。 - Jenna Zeigen
同时发生的其他事情仍然可能导致多毫秒延迟抖动,这对于许多目的来说可能过于严重,即使在测量人类反应时也是如此(因为它们会降低准确性)。不幸的是,这不能从用户空间解决--请查看实时Linux,寻找一个相对容易使用并具有软实时能力的操作系统。但是,很遗憾,这可能不是您自己可以解决的问题。建议寻求计算机科学研究生的帮助。 - agf
6
如果GUI事件和常见操作系统对您的需求来说太慢或不可靠,那么Python可能不是您要开始学习的编程语言。 - Ferdinand Beyer
6个回答

23

当人们谈论实时计算时,他们的意思是说中断(通常由定时器触发)到应用程序代码处理该中断被运行的延迟时间既小又可预测。这意味着控制过程可以以非常精确的时间间隔重复运行,或者像您的情况一样,外部事件可以被非常精确地计时。延迟变化通常称为“抖动”--最大抖动1ms表示重复到达的中断将有一个响应延迟,最多相差1ms。

“小”和“可预测”都是相对的术语,当人们谈论实时性能时,他们可能指的是最大抖动1μs(例如为电力传输搭建逆变器的人们关心此类性能),也可能指的是最大抖动几毫秒。这完全取决于应用程序的要求。

无论如何,Python可能不是这项工作的正确工具,原因如下:

  • Python主要在桌面操作系统上运行。桌面操作系统对最大抖动有一个下限;在Windows的情况下,它是几秒钟。几秒钟的事件不经常发生,每天或两天一次,而且你很不幸会碰到与你尝试测量的东西巧合的情况,但迟滞在几百毫秒的区域更经常发生,可能每小时一次,而几十毫秒的迟滞则相当频繁。桌面Linux的数字可能类似,尽管您可以应用不同的编译时选项和补丁集来改善Linux内核的情况--请参阅Google PREEMPT_RT_FULL。
  • Python的全局解释器锁定(GIL)使得延迟是不确定的。当Python决定需要运行垃圾收集器时,程序会被停止直到它完成。您可以通过仔细的内存管理和精心设置垃圾收集器参数来避免这种情况,但取决于您使用的库,您可能无法做到这一点。
  • Python内存管理的其他特性使得确定性延迟变得困难。大多数实时系统避免堆分配(如C的malloc或C++的new)因为它们所需的时间是不可预测的。Python很好地隐藏了这一点,使得控制延迟变得非常困难。再加上使用许多现成库只会使情况更加恶化。
  • 同样地,在实时进程中,重要的是确保所有内存都保存在物理RAM中,而不是被交换到虚拟内存。在Python中没有很好的控制方式,特别是在Windows上运行(在Linux上你可能能够在某个地方调用mlockall,但任何新的分配都将破坏这个设置)。

我有一个更基本的问题。您没有说明您的按钮是物理按钮还是屏幕上的按钮。如果是屏幕上的按钮,操作系统将会在物理鼠标按钮按下和事件到达您的Python应用程序之间强加一个不可预测的延迟。您将如何解决这个问题?在没有更准确的衡量方法的情况下,您将如何知道是否存在这种延迟?


我知道这是一个旧帖子,但在您的第四点中提到了“(在Linux上,您可能可以在某个地方适合调用mlockall,但任何新的分配都会引起麻烦)”,您能解释一下为什么吗?我正在寻求实现类似的东西,这引起了一些担忧。谢谢! - Geoherna
2
这可能是我误解了 mlockall() - 我认为有一种选项可以锁定当前和未来在物理内存中的分配,这样未来的分配就不会干扰它(至少减少干扰 - 动态分配仍然是不确定的,并且随着内存填充而变得更糟)。 - Tom

5
Python并不是一个实时语言,因为它有太多的库和函数,无法达到裸机快速。如果你已经在使用操作系统而不是嵌入式系统,那么你已经失去了很多真正的实时能力。(当我听到"实时"这个词时,我想到的是VHDL代码流过FPGA电线的时间。其他人则用它来表示“我按下按钮,它会立即执行某些操作”,从我的缓慢人类角度来看,是瞬间完成的。我假设你是使用后一种解释方式的“实时”)。通过刺激显示和按键检测,我认为你是指像试验这样的东西,你向一个人展示一张图片,并让他们点击一个按钮来识别该图片或确认他们已经看到它-也许是为了测试反应速度。除非你担心精度达到毫秒级(这应该可以忽略不计,与人类反应时间相比),否则你可以使用Python进行此类测试。要处理GUI,请查看Tkinter:http://www.pythonware.com/library/tkinter/introduction/。要处理刺激和按键之间的时间,请查看时间文档:http://docs.python.org/library/time.html。祝你好运!

谢谢。我已经将这件事编码得很好了,我只想尽可能地让它准确。你有任何想法会导致滞后吗?至于反应时间的大小,可能需要一个受试者大约600毫秒才能对呈现的单词做出反应,但我们所关注的效果可能在35-45毫秒左右。 - Jenna Zeigen
你可以对你的代码进行性能分析 (http://docs.python.org/library/profile.html),以了解它的延迟情况。我认为你应该编译一些关于软件时间延迟的统计数据,以便将其纳入结果误差范围之内。 - user671110
1
这里的措辞又让人感到困惑了:“基本快速”。实时并不意味着快速,只是对于给定事件有严格的最大响应时间。许多系统都很快,许多非实时系统比大多数实时系统更快,但仍可能会遇到偶发的不可接受的延迟。 - nyholku

4
大多数常见操作系统的中断都太过不稳定,无论使用何种编程语言,都可能会破坏您的实验计时。Python 也会增加其自身的不可靠性。Windows 中的中断尤其糟糕。在 Windows 中,大多数中断的服务时间约为 4 毫秒,但偶尔会有一个中断持续时间超过 35 毫秒(Windows 7)。
我建议尝试 PsycoPy 应用程序来查看是否适合您。它通过尝试使图形卡在 openGL 中工作来解决此问题,但是其代码仍在图形卡之外运行,并受操作系统中断的影响。您现有的 Python 代码可能与 PsycoPy 不兼容,但至少您仍然可以保持在 Python 中。PsycoPy 特别擅长显示没有时间问题的视觉刺激。请参阅其文档中的此页面,以了解如何处理按钮按下:http://www.psychopy.org/api/event.html 要正确解决您的问题,您需要实时操作系统,例如 LinuxRT 或 QNX。您可以在其中之一中尝试您的 Python 应用程序,以查看在实时环境中运行 Python 是否足够良好,但即便是 Python 也会引入变异性。如果 Python 决定进行垃圾回收,您将会出现故障。Python 本身不是实时的。
国家仪器公司销售了一套设备,可以让您在非常易于使用的编程语言 LabviewRT 中实时编程。LabviewRT 将您的代码推入一个 FPGA 子卡中,以实时操作。它很昂贵。
我强烈建议您不要只是将此问题最小化,而要解决它,否则,您的审阅人员将感到不舒服。

4
因为您正在尝试以毫秒精度获得时间延迟的科学测量结果,所以我不建议使用任何在通用计算机上进行时间分片的进程。无论是用C、Java、还是Python实现,如果它在时间共享模式下运行,那么结果如何可验证呢?您可能需要证明CPU在测量期间从未中断该进程,从而扭曲了结果。
看起来您可能需要构建一个专用设备,其中包含一个时钟电路,以已知速率滴答,并可以测量刺激和响应之间发生的离散滴答数。然后该设备可以由没有此类时间约束的软件控制。也许您应该将这个问题发布到电子工程交换平台。
如果没有专用设备,您将不得不开发真正的实时软件,在现代操作系统中,它在内核中运行,不受任务切换的影响。这并不容易做到,需要付出大量的努力才能做好。我猜花费的时间可能比为您的目的构建一个专用的软件可控设备要多。

这些问题已经被充分理解,也有众所周知的工具来处理它们;你可以从谷歌搜索“进程优先级”开始。 - Tom

2
如果您正在Linux机器上运行Python代码,请使内核低延迟(抢占式)。 在编译内核时,有一个标志可以实现此功能。
确保机器上运行的其他进程最小化,以便它们不会中断内核。
将更高的任务优先级分配给您的Python脚本。

还有?!? 怎样消除非确定性垃圾回收? - user9826550
通过关闭它。有人指出系统必须是低延迟的,仅用于大约50次测试的回合,然后可以花费一些时间变得不那么响应。只有恒定的软实时行为才难以通过非确定性GC实现,只要可以暂时关闭它即可。 - yeoman

1
  1. 在实时操作系统或调整过的Linux上运行Python解释器。
  2. 在实验期间关闭垃圾收集器,之后再打开。
  3. 也许在实验结束后主动触发一次垃圾回收循环。

此外,请记住显示图像不是瞬间完成的。您必须将实验与监视器的垂直回传阶段同步(即显示内容帧的最后一行传输和下一帧的第一行之间的暂停)。

我会在传输包含候选人应该反应的帧的vsync阶段开始计时器。

而且,为了得到绝对反应时间而不仅仅是具有~半帧偏移的可比较结果(由于显示器内容的非瞬时出现而引起的约10毫秒@ 60Hz),必须记住图像将至少部分可见早于此。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接