OpenVibe|为采集服务器创建新驱动程序

为采集服务器创建新驱动程序
介绍
此页面指驱动开发人员,并显示如何为 OpenViBE采集服务器创建新的采集驱动程序。驱动程序是一个与硬件获取外围相互作用的对象,并以采集服务器的方式格式化已获得的测量和信息。一旦采集服务器收到测量和信息,它能够将其发送到一个或多个已连接的客户端。
硬件和驱动程序之间的连接类型对采集服务器来说并不重要。某些硬件制造商将提供 API,以便驱动程序通过物理连接(可能是 USB、串行端口等)直接访问外围区域。一些将提供专有的采集服务器,允许通过 TCP/IP 连接实时流式传输测量结果。无论您的硬件制造商提供什么,OpenViBE 驱动程序负责从设备中收集必要的信息和数据,并根据 OpenViBE 的格式对所有这些信息和数据进行格式化。
实施起点
基本上有两种方法来实现新的驱动程序。一种是将现有驱动程序(如通用振荡器或某些驱动程序用于真实设备)并修改其代码以适应您的目的。另一种选择是从"empty"驱动程序开始。为此,我们提供了一个名为Skeleton-generator的开发工具,它会生成空模板文件,您可以填写以满足您的需求。
在下面,我们首先描述驱动程序的主要部分,然后用发电机解释过程。许多开发人员可能会发现,首先与一些现有驱动程序一起玩,查看其代码,然后使用此文档作为解释驱动程序内容的参考,会更加直观。
在这里插入图片描述

驱动程序是如何使用的?
首先,驱动程序必须向采集服务器声明其名称。这将用于采集服务器GUI,以轻松识别用户正在使用的硬件。因此,您应该向驱动程序提供精确的名称,例如硬件制造商名称和硬件型号名称。
驱动程序主要处理两种数据:
标题
标题是数据中不会随时间变化的部分。它包含有关正在进行的实验的几个标识符、有关正在获取的渠道的信息等。
缓冲区
缓冲区是随时间变化的数据部分。它包含特定时间段内每个通道的不同样本,这取决于每个发送通道的样本数量。此值在初始化阶段授予驱动程序。
要处理这些数据,采集服务器在执行的不同阶段的驱动程序。
配置
配置阶段可供驱动程序使用,以便从硬件中无法获得的用户请求信息。根据硬件的不同,所有信息都可能在流式传输的数据中找到,从而产生不可配置的驱动程序(如果可能,对于采集服务器用户来说,这可能更容易)。例如,具有物理连接的驱动程序可能不提供主题年龄或性别。如果需要提供此类信息,则此类驱动程序将需要配置阶段。
初始化/非硝化
在初始化阶段,驱动程序向硬件请求就绪状态。 一旦驱动程序被初始化(这意味着 OpenViBEAcquisitionServer::IDriver::initialize 返回),驱动程序应该有一个完整的标题准备发送到采集服务器。 驱动程序的 OpenViBEAcquisitionServer::IDriver::loop 会定期调用,以便驱动程序能够保持与设备的连接,并在需要时丢弃一些数据。
采集
采集阶段在调用 OpenViBEAcquisitionServer::IDriver::start 时出现。 从这个阶段开始,驱动程序会定期请求在 OpenViBEAcquisitionServer::IDriver::loop 函数中提供新数据。
以下为驱动程序自动机的示意图表示:
驱动程序自动机在这里插入图片描述
驱动程序自动机
使用骨架生成器
发电机将为您提供空.cpp/.h模板,用于实现您的驱动程序。这是通过自定义现有驱动程序来替代驱动程序。
发生器需要以下信息:
作者姓名
公司/隶属关系
驱动程序名称

类名称
最小值和最大通道计数
采样频率
生成文件的目录
并将生成以下文件:
驱动程序类代码(.h 和.cpp)
配置类代码(.h 和.cpp)
GtkBuilder 接口文件 (.ui)
一个.txt文件,解释如何使用生成的文件。
在这里插入图片描述

skeleton generatorGUI
每个字段都有自己的条件,使用"帮助"按钮进行详细示例。填充所有字段后,您必须按下专用按钮检查它。将显示报告,要求您修改错误的字段(如果有)。如果一切都很好,您可以按下按钮"生成!" 生成文件。
编码驱动程序
编码驱动程序包括实施 OpenViBEAcquisitionServer::IDriver 对象。
让我们看看驱动程序,例如从生成器获得的驱动程序。
标题
标题将看起来像这样:
#include “…/ovasIDriver.h”
#include “…/ovasCHeader.h”
#include <openvibe/ov_all.h>
namespace OpenViBEAcquisitionServer
{
class CDriverTestSkGenerator : public OpenViBEAcquisitionServer::IDriver
{
public:
CDriverTestSkGenerator(OpenViBEAcquisitionServer ::IDriverContext& rDriverContext);
virtual ~CDriverTestSkGenerator(void);
virtual const char* getName(void);
virtual OpenViBE::boolean initialize(
const OpenViBE::uint32 ui32SampleCountPerSentBlock,
OpenViBEAcquisitionServer::IDriverCallback& rCallback);
virtual OpenViBE::boolean uninitialize(void);
virtual OpenViBE::boolean start(void);
virtual OpenViBE::boolean stop(void);
virtual OpenViBE::boolean loop(void);
virtual OpenViBE::boolean isConfigurable(void);
virtual OpenViBE::boolean configure(void);
virtual const OpenViBEAcquisitionServer::IHeader* getHeader(void)
{
return &m_oHeader;
}

            virtual OpenViBE::boolean isFlagSet(
                    const OpenViBEAcquisitionServer::EDriverFlag eFlag) const
            {
                    return eFlag==DriverFlag_IsUnstable;
            }
    protected:
            SettingsHelper m_oSettings;
            OpenViBEAcquisitionServer::IDriverCallback* m_pCallback;
            // Replace this generic Header with any specific
            // header you might have written
            OpenViBEAcquisitionServer::CHeader m_oHeader;
            OpenViBE::uint32 m_ui32SampleCountPerSentBlock;
            OpenViBE::float32* m_pSample;
    private:
            /*
             * Insert here all specific attributes, such as USB port
             * number or device ID.
             * Example :
             */
            // OpenViBE::uint32 m_ui32USBPort;
    };

};
我们现在将详细描述这些功能的实现。 实施可以分为 3 个阶段:初步、配置、获取。
初步
cpp 文件的第一部分包含构造函数、析构函数和 getName 函数。 代码相对简单。
CDriverTestSkGenerator::CDriverTestSkGenerator(IDriverContext& rDriverContext)
:IDriver(rDriverContext)
,m_oSettings(“AcquisitionServer_Driver_TestSkGenerator”, m_rDriverContext.getConfigurationManager())
,m_pCallback(NULL)
,m_ui32SampleCountPerSentBlock(0)
,m_pSample(NULL)
{
m_oHeader.setSamplingFrequency(128);
m_oHeader.setChannelCount(4);
// The following class allows saving and loading driver settings from the acquisition server .conf file
m_oSettings.add(“Header”, &m_oHeader);
// To save your custom driver settings, register each variable to the SettingsHelper
//m_oSettings.add(“SettingName”, &variable);
m_oSettings.load();
}
CDriverTestSkGenerator::~CDriverTestSkGenerator(void)
{
}
const char* CDriverTestSkGenerator::getName(void)
{
return “Simple test driver (p1)”;
}
配置
配置阶段调用专用 GUI。 CConfiguration 对象用来自 GUI 的信息填充标头:主题标识符、通道数、通道名称、采样频率等。如果设备具有用户可设置的参数,这些参数预计会从服务器的一次运行持续到另一次运行,则驱动程序 可以声明这些和它们的值(见上文;并由采集服务器自动保存/加载它们)。 这是使用帮助成员完成的。 这个类的用法可以从现有的drivers.SettingsHelper m_oSettings; 中看出。
boolean CDriverTestSkGenerator::isConfigurable(void)
{
return true; // change to false if your device is not configurable
}
boolean CDriverTestSkGenerator::configure(void)
{
CConfigurationTestSkGenerator m_oConfiguration(m_rDriverContext,
OpenViBE::Directories::getDataDir() + “/applications/acquisition-server/interface-TestSkGenerator.ui”);
if(!m_oConfiguration.configure(m_oHeader))
{
return false;
}
m_oSettings.save();
return true;
}
获取
要获取数据,我们必须首先初始化驱动程序。 初始化函数将通过给定的回调由获取服务器调用。 该回调稍后用于向采集服务器提供样本缓冲区。 如果硬件需要初始化,请将专用代码放入函数中。 uninitialize 函数正确地取消分配所有内容。loop()initialize
boolean CDriverTestSkGenerator::initialize(
const uint32 ui32SampleCountPerSentBlock,
IDriverCallback& rCallback)
{
if(m_rDriverContext.isConnected()) return false;
if(!m_oHeader.isChannelCountSet()
//!m_oHeader.isSamplingFrequencySet()) return false;
// Builds up a buffer to store
// acquired samples. This buffer
// will be sent to the acquisition
// server later…
m_pSample=new float32[m_oHeader.getChannelCount()
*ui32SampleCountPerSentBlock];
if(!m_pSample)
{
delete [] m_pSample;
m_pSample=NULL;
return false;
}
// …
// initialize hardware and get
// available header information
// from it
// …
// Saves parameters
m_pCallback=&rCallback;
m_ui32SampleCountPerSentBlock=ui32SampleCountPerSentBlock;
return true;
}
boolean CDriverTestSkGenerator::uninitialize(void)
{
if(!m_rDriverContext.isConnected()) return false;
if(m_rDriverContext.isStarted()) return false;
// …
// uninitialize hardware here
// …
delete [] m_pSample;
m_pSample=NULL;
m_pCallback=NULL;
return true;
}
一旦驱动程序被初始化,采集就可以开始了。 start 和 stop 函数处理连接并要求硬件开始/停止发送数据。
boolean CDriverTestSkGenerator::start(void)
{
if(!m_rDriverContext.isConnected()) return false;
if(m_rDriverContext.isStarted()) return false;
// …
// request hardware to start
// sending data
// …
return true;
}
boolean CDriverTestSkGenerator::stop(void)
{
if(!m_rDriverContext.isConnected()) return false;
if(!m_rDriverContext.isStarted()) return false;
// …
// request the hardware to stop
// sending data
// …
return true;
}
最后,循环函数读取数据并将其存储在缓冲区中。 连接(初始化)驱动程序后,采集服务器会尽快调用此函数。
boolean CDriverTestSkGenerator::loop(void)
{
if(!m_rDriverContext.isConnected()) return false;
if(!m_rDriverContext.isStarted()) return true;
OpenViBE::CStimulationSet l_oStimulationSet;
// …
// receive samples from hardware
// put them the correct way in the sample array
// whether the buffer is full, send it to the acquisition server
//…
m_pCallback->setSamples(m_pSample);
// …
// receive events from hardware
// and put them the correct way in a CStimulationSet object
//…
m_pCallback->setStimulationSet(l_oStimulationSet);
return true;
}
对于驱动程序的简单工作示例,请查看通用振荡器。此驱动程序不连接到任何硬件。相反,它使用鼻窦信号生成样品本身。这可以很容易地调整,以满足您的任何特定驱动程序的需求,也可以用来测试您的 OpenViBE 平台。
将驱动程序包括到服务器
为了让驱动程序在采集服务器中可见,您必须注册驱动程序。发生的情况如下:
将您的 .h / .cpp 文件放入文件夹中,contrib/plugins/server-drivers/your-driver-name/src/.
编辑 contrib/common/contribAcquisitionServer.inl。 该内联文件使用 Acquisition Server 编译并注册驱动程序。
编辑 contrib/common/contribAcquisitionServer.cmake。 此脚本应该使您的驱动程序对构建可见。
编辑 contrib/common/contribAcquisitionServerLinkLibs.cmake。 此脚本应调用与您的驱动程序相关的 CMake 查找脚本。 被调用的脚本应检查驱动程序所需的组件(如果有)是否存在于系统中,如果存在,则将它们添加到构建中。 cmake 文件应该使编译所需的任何头文件和库可见,并将运行时所需的任何文件(例如 .dlls)安装到 dist/bin/ 目录。 请在您的驱动程序中使用包含保护,以确保构建不会在其依赖项不可用时尝试编译您的驱动程序。
您可以使用与以前的驱动程序相关的代码作为示例。
内联文件通常有类似以下为您的代码,
#if TARGET_HAS_MyDriverStuff
#include “your-driver-name/ovasCDriverYourDriverName.h”
#endif
// …
void initiateContributions(…)
{
// …
#if TARGET_HAS_MyDriverStuff
m_vDriver.push_back(new OpenViBEAcquisitionServer::CDriverYourDriverName(m_pAcquisitionServer->getDriverContext()));
#endif
// …
}
如果您使用生成器,请查看readme文件以了解更多详细信息。
最佳实践
为了确保您的驱动程序具有高质量和良好的表现,应采用以下做法,
在操作过程中,驱动程序不应依赖任何类型的呼叫,因为其精度无法保证在非实时操作系统上。相反,驱动程序应等待您的设备提供硬件唤醒事件,指示有更多的数据可供阅读,然后阅读并随回调一起传递,并尽快从呼叫中返回。硬件事件等待应有超时。loop()sleep()
如果驱动程序创建任何线程,这些线程应在成员函数中启动并终止。不要在驱动程序构造器中启动线程。这样,对于未使用驱动程序的用户,线程不会不必要地启动。initialize()uninitialize()
驱动程序应携带所有在呼叫中具有不可忽略的延迟的启动操作,或者如果不可能,在 。该功能预计将在呼叫到呼叫(包括第一次呼叫)中稳定运行。initialize()start()loop()
在正常情况下,驱动程序应该在禁用服务器漂移校正功能的情况下正常工作。 如果不是,则可能表明存在应解决的问题。 例如,如果您在服务器中按下 Play 后立即看到稳定的负漂移,这可能表明第一次调用具有显着的延迟,它不应该有(见上文)。 与 OpenViBE 捆绑的基本 BCI 示例(运动图像、P300、SSVEP)应该都可以在没有漂移校正的情况下工作。
如果您的驱动程序依赖于不属于源代码树的任何材料(例如专有 dll 和头文件),请使用包含保护将所有 .cpp/.h 文件包围起来,以便在这些材料不存在的情况下仍然可以编译 OpenViBE。 检测材料存在的 CMake 脚本应该声明守卫。
最后,应使用真实科目和使用案例进行实时测试,以确保驱动程序工作。
符合这些做法对于所有打算考虑纳入 OpenViBE 分销的驱动程序尤其重要。
高级开发
您的驱动程序可能是功能性的,但不完整。您应该能够连接它并在采集服务器中启动它,但更多的发展是可能的,以改善您的驱动程序。以下图解将帮助您进行更高级的发展:
教程:配置新驱动程序Tutorial 2: Configuring a new driver | OpenViBE (inria.fr):如何设置专用 GUI,以便用户可以配置驱动程序。
教程:使用内置的阻抗检查器Tutorial 4: Using the built-in impedance checker | OpenViBE (inria.fr):如果您的设备有能力,如何使用给定的阻抗检查器机制。
教程:抖动监测和漂移修正Tutorial 3: Jitter monitoring and drift correction | OpenViBE (inria.fr):采集在某种程度上偏离了其理论采样率?通过抖动监控和漂移校正来纠正它。但是,如果可能,驱动程序应工作时不会进行漂移校正,而大漂移可能表明存在问题。

有需求可以和我们联系
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值