Visual c++ 使用NI DAQ卡读取力传感器数据时如何设置采样率

Visual c++ 使用NI DAQ卡读取力传感器数据时如何设置采样率,visual-c++,sensors,sampling,nidaqmx,Visual C++,Sensors,Sampling,Nidaqmx,我正在使用NI DAQ卡读取力传感器数据,我能够完美地读取数据,但我不知道如何设置所需的采样率。现在,当我检查采样率时,它会给我随机值,比如有时是500hz,有时是50000hz 这是我用来读取力传感器数据和计算采样率的代码 main.h file int Run_main(void *pUserData) { /** to visual data **/ CAdmittanceDlg* pDlg = (CAdmittanceDlg*)pUserData

我正在使用NI DAQ卡读取力传感器数据,我能够完美地读取数据,但我不知道如何设置所需的采样率。现在,当我检查采样率时,它会给我随机值,比如有时是500hz,有时是50000hz

这是我用来读取力传感器数据和计算采样率的代码

main.h file
 int Run_main(void *pUserData)
    {
        /** to visual data **/
        CAdmittanceDlg* pDlg = (CAdmittanceDlg*)pUserData;
        /** timer for calculating sampling rate **/
        performanceTest timer;
        /*** Force Sensor **/
        ForceSensor sensor1("FT8682.cal","FT_Sensor1","dev3/ai0:6",2,1.0e4);
        std::vector<double> force;
        while(1)
        {
            /*** start time  ***/
            timer.setStartTimeNow();
            /*** reading force sensor data  ***/
            force=sensor1.readForce();
            /*** visualize data in GUI  ***/
            pDlg->setCustomData(0,"force_x ",force[0]);
            pDlg->setCustomData(1,"force_y ",force[1]);
            pDlg->setCustomData(2,"force_z ",force[2]);
            pDlg->setCustomData(3,"position ",currentPosition);
            timer.increaseFrequencyCount();
            /*** end time  ***/
            pDlg->setCustomData(4,"Sampling rate ",1/timer.getElapsedTime());
        }


        return 1;
    }

//here is ForceSensor.h file
class ForceSensor
{
public:

    DAQmx board;
    Calibration *cal;

    float bias[7];
    std::vector<double> f;

    ForceSensor(std::string calibrationFile, std::string task,
        std::string device, uInt64 numberOfSamples = 1000, float64 samplingRate = 1.0e4):
    board(task, device, numberOfSamples, samplingRate),
        f(6, 0)
    {
        board.initRead();
        cal = createCalibration(calibrationFile.c_str(), 1);
        if (!cal) throw std::runtime_error(calibrationFile + " couldn't be opened");
        board.readVoltage(2); // to get reed of zeros in the first batch of samples
        board.readVoltage(1000); // read big number of samples to determine the bias

        std::copy (board.da.cbegin(), board.da.cend(), std::ostream_iterator<double>(std::cout, " "));
        std::cout << std::endl;

        Bias(cal, board.da.data());
    }

    ~ForceSensor(void){}

    const std::vector<double> & readForce(){
        auto forces = std::vector<float>(6, 0);


        board.readVoltage(2);

        ConvertToFT(cal, board.da.data(), forces.data());

        std::transform(forces.cbegin(), forces.cend(),
            f.begin(),
            [](float a){ return static_cast<double>(a);});

        return f;
    }


};

//DAQmx.h file
class DAQmx {
    float64 MaxVoltage;
    TaskHandle taskHandle;
    TaskHandle counterHandle;
    std::string taskName;
    std::string device;
    float64 samplingRate;

public:
    std::vector <float> da;
    uInt64 numberOfSamples;


    DAQmx(std::string task, std::string device, uInt64 numberOfSamples = 1000, float64 samplingRate = 1.0e4):
        taskName(task), device(device), samplingRate(samplingRate), numberOfSamples(numberOfSamples),
        da(7, 0.0f)
    {
         MaxVoltage = 10.0;
    }

    ~DAQmx()
    {
        if( taskHandle == 0 )  return;
        DAQmxStopTask(taskHandle);
        DAQmxClearTask(taskHandle);
    }

    void CheckErr(int32 error, std::string functionName = "")
    {
        char        errBuff[2048]={'\0'};

        if( DAQmxFailed(error) ){
            DAQmxGetExtendedErrorInfo(errBuff, 2048);

            if( taskHandle!=0 )  {
                DAQmxStopTask(taskHandle);
                DAQmxClearTask(taskHandle);
            }
            std::cerr << functionName << std::endl;
            throw std::runtime_error(errBuff);
        }
    }


    void initRead()
    {
        CheckErr(DAQmxCreateTask(taskName.c_str(), &taskHandle));
        CheckErr(DAQmxCreateAIVoltageChan(taskHandle, device.c_str(),"", DAQmx_Val_Diff ,-10.0,10.0,DAQmx_Val_Volts,NULL));
//        CheckErr(DAQmxCfgSampClkTiming(taskHandle, "" , samplingRate, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, numberOfSamples));
        CheckErr(DAQmxCfgSampClkTiming(taskHandle, "OnboardClock" , samplingRate, DAQmx_Val_Rising, DAQmx_Val_ContSamps, numberOfSamples*10));
        CheckErr(DAQmxSetReadRelativeTo(taskHandle, DAQmx_Val_MostRecentSamp ));
        CheckErr(DAQmxSetReadOffset(taskHandle,-1));
        CheckErr(DAQmxStartTask(taskHandle));
    }

    void initWrite()
    {
        CheckErr(DAQmxCreateTask(taskName.c_str(), &taskHandle));
        CheckErr(DAQmxCreateAOVoltageChan(taskHandle, device.c_str(),"",-10.0, 10.0, DAQmx_Val_Volts,""));
        CheckErr(DAQmxStartTask(taskHandle));
    }


    int32 readVoltage (uInt64 samples = 0, bool32 fillMode = DAQmx_Val_GroupByScanNumber) //the other option is DAQmx_Val_GroupByScanNumber
    {
        int32   read; // samples actually read
        const float64 timeOut = 10;
        if (samples == 0) samples = numberOfSamples;
        std::vector<float64> voltagesRaw(7*samples);

        CheckErr(DAQmxReadAnalogF64(taskHandle, samples, timeOut, fillMode,
                                    voltagesRaw.data(), 7*samples, &read, NULL));
//        DAQmxStopTask(taskHandle);
        if (read < samples)
            throw std::runtime_error ("DAQmx::readVoltage(): couldn't read all the samples,"
                                      "requested: "  + std::to_string(static_cast<long long>(samples)) +
                                      ", actually read: " + std::to_string(static_cast<long long>(read)));

        //we change it
        for(int axis=0;axis < 7; axis++)
        {
            double temp = 0.0;
            for(int i=0;i<read;i++)
            {
                temp += voltagesRaw[i*7+axis];
            }
            da[axis] = temp / read;
        }


        return read;
    }


    void writeVoltage(float64 value)
    {
        if (value > MaxVoltage) value = MaxVoltage;
        if (value < -MaxVoltage) value = -MaxVoltage;
        const float64 timeOut = 10;
            //A value of 0 indicates to try once to read the requested samples.
            //If all the requested samples are read, the function is successful.
            //Otherwise, the function returns a timeout error and returns the samples that were actually read.

        float64 data[1] = {value};
        int32 written;

        CheckErr(DAQmxWriteAnalogF64(taskHandle, 1, 1, timeOut, DAQmx_Val_GroupByChannel, data, &written, NULL));

        DAQmxStopTask(taskHandle);
    }

};
main.h文件
int Run_main(无效*pUserData)
{
/**可视化数据**/
CAdmittanceDlg*pDlg=(CAdmittanceDlg*)pUserData;
/**用于计算采样率的计时器**/
性能测试计时器;
/***力传感器**/
力传感器传感器1(“FT8682.cal”、“FT_传感器1”、“dev3/ai0:6”、2,1.0e4);
矢量力;
而(1)
{
/***开始时间***/
timer.setStartTimeNow();
/***读取力传感器数据***/
力=传感器1.readForce();
/***在GUI中可视化数据***/
pDlg->setCustomData(0,“力_x”,力[0]);
pDlg->setCustomData(1,“强制”,强制[1]);
pDlg->setCustomData(2,“力”,力[2]);
pDlg->setCustomData(3,“位置”,当前位置);
timer.increaseFrequencyCount();
/***结束时间***/
pDlg->setCustomData(4,“采样率”,1/timer.getElapsedTime());
}
返回1;
}
//这是ForceSensor.h文件
类力传感器
{
公众:
DAQmx板;
校准*校准;
浮动偏差[7];
std::向量f;
ForceSensor(标准::字符串校准文件,标准::字符串任务,
标准::字符串设备,uInt64 numberOfSamples=1000,浮点64采样率=1.0e4):
电路板(任务、设备、样本数量、采样),
f(6,0)
{
board.initRead();
cal=createCalibration(calibrationFile.c_str(),1);
如果(!cal)抛出std::runtime_错误(calibrationFile+“无法打开”);
board.readVoltage(2);//获取第一批样本中的零簧片
board.readVoltage(1000);//读取大量样本以确定偏差
std::copy(board.da.cbegin()、board.da.cend()、std::ostream_迭代器(std::cout“”);
标准::cout setCustomData(2,“force_z”,force[2]);
pDlg->setCustomData(3,“位置”,当前位置);
pDlg->setCustomData(5,“设备采样率”,采样率);
timer.increaseFrequencyCount();
计数++;
如果(计数=1000)
{
pDlg->setCustomData(4,“时间流逝”,count/timer.getElapsedTime());
计数=0;
timer.setStartTimeNow();
}
/***结束时间***/
}
返回1;
}
//这是NiMotion.file
类运动{
u8 boardID;//板标识号
u8轴;//轴号
u16 csr;//通信状态寄存器
u16 axisStatus;//轴状态
u16完成;
int32 encoderCounts;//当前位置[计数]
int32编码器计数起始位置;
双位置;//以米为单位的位置
博尔先读;
公众:
NiMotion(u8 boardID=1,u8轴=NIMC_轴1):boardID(boardID),轴(轴),csr(0)
{
init();
}
~NiMotion(){enableMotor(false);}
void init(){
CheckErr(flex_initialize_controller(boardID,nullptr));//使用默认设置
校验错误(flex_read_pos_rtn(板ID、轴和编码计数));
encoderCountsStartPosition=encoderCounts;
使能电机(假);
先读=真;
loadConfiguration();
}
双toCm(i32计数){返回计数*countsToCm;}
i32 toCounts(双Cm){返回(Cm/countsToCm);}
双读取位置(){
//校验错误(flex_read_pos_rtn(板ID、轴和编码计数));
CheckErr(flex_read_encoder_rtn(boardID、axis和encoderCounts));
如果(先读)
{
encoderCountsStartPosition=encoderCounts;
首先读取=错误;
}
encoderCounts-=encoderCountsStartPosition;
位置=编码器计数*计数到厘米;
返回位置;
}
无效启用马达(布尔状态)
{
如果(州)

CheckErr(flex\u set\u inhibit\u output\u momo)(boardID,1您的问题分为两部分:

  • “我对观察到的采样率感到惊讶。”
  • “我想让我的控制循环速度更快。”
  • 1.采样率与应用程序吞吐量 在
    Run\u main
    中,您有

    pDlg->setCustomData(4,"Sampling rate ",1/timer.getElapsedTime());
    
    这似乎就是“检查采样率”的方式。这不是报告采样率,而是报告应用程序吞吐量

    查询采样率 如果要向驱动程序询问设备的采样率,请使用

    int32 DllExport __CFUNC DAQmxGetSampClkRate(TaskHandle taskHandle, float64 *data);
    
    了解应用程序吞吐量 虽然硬件总是以恒定速率采样数据,但它仍然需要将数据传输到应用程序的内存中。该速率并不总是匹配——有时较慢,有时较快,具体取决于其他操作系统任务和应用程序接收的CPU数量

    重要的是,平均应用程序吞吐量与硬件的采样率相匹配。如果应用程序无法跟上,那么最终硬件将填充其板载和驱动程序缓冲区并返回错误

    调整应用程序吞吐量 NIDAQMX有几种不同的方法来控制硬件向应用程序传输数据的方式和时间

    • 使用在采集N个样本时触发的:

      int32 __CFUNC  DAQmxRegisterEveryNSamplesEvent(TaskHandle task, int32 everyNsamplesEventType, uInt32 nSamples, uInt32 options, DAQmxEveryNSamplesEventCallbackPtr callbackFunction, void *callbackData);
      
    • 在将设备传输到主机之前,必须先调整设备的板载内存

      int32 DllExport __CFUNC DAQmxSetAIDataXferMech(TaskHandle taskHandle, const char channel[], int32 data);
      
    有关更多详细信息,请参阅

    2.实施有效的控制回路 一旦你有了依赖于输入的输出,你就有了一个控制回路,你正在实现a。可以获得更快的输入,计算一个新的设定点,并将新的设定点发送到输出,b
    int32 __CFUNC  DAQmxRegisterEveryNSamplesEvent(TaskHandle task, int32 everyNsamplesEventType, uInt32 nSamples, uInt32 options, DAQmxEveryNSamplesEventCallbackPtr callbackFunction, void *callbackData);
    
    int32 DllExport __CFUNC DAQmxSetAIDataXferMech(TaskHandle taskHandle, const char channel[], int32 data);