zoukankan      html  css  js  c++  java
  • LPMS-B2 数据采集源码分析

    本文是针对 LPMS-B2 数据采集源码进行分析, LPMS-B2 是 LP公司出品的一款IMU,最近用到特别总结。

    源码的数据采集程序,可见第38行其中使用了pollData和update进行数据采集。

    void LpmsSensorManager::run(void)
    {
        MicroMeasure mm;
    
        float prevTimestamp = 0.0f;
        int deviceType = 0;
    
    #ifdef _WIN32	
        ce.connect();
        // be.connect();
    #endif	
    
    #ifdef ANDROID
        LOGV("[LpmsSensorManager] Thread running
    ");
    #endif
    
        mm.reset();
        int sleepFlag = 0;
        while (stopped == false) {
            switch (managerState) {
            case SMANAGER_MEASURE:
                lm.lock();
                for (auto i = sensorList.begin(); i != sensorList.end(); i++) {
                    (*i)->pollData();//数据采集
                }
    
    #ifdef _WIN32
                ce.poll();
    #endif
    
                lm.unlock();
    
                if (mm.measure() > threadDelay) {
                    mm.reset();
    
                    lm.lock();
                    for (auto i = sensorList.begin(); i != sensorList.end(); i++) {
                        (*i)->update(); //数据采集
                    }
                    lm.unlock();
                } else {
                    std::this_thread::sleep_for(std::chrono::microseconds(100));
                }
                break;
    
            case SMANAGER_LIST:
                deviceList.clear();
    
    #ifdef _WIN32
                ce.listDevices(&deviceList);
                // be.listDevices(&deviceList);			
    #endif
                if (managerState != SMANAGER_LIST)
                    break;
                if (scan_serial_ports_ == true)
                {
                    if (verbose)
                        logd(TAG, "List RS2323 devices
    ");
                    LpmsRS232::listDevices(&deviceList);
                }
    
                // if (managerState != SMANAGER_LIST)
                    // break;
    			// LpmsTcp::listDevices(&deviceList);
    
    #ifdef BUILD_LPMS_U
                if (managerState != SMANAGER_LIST)
                    break;
                LpmsU::listDevices(&deviceList);
    #endif
    #ifdef _WIN32
                if (managerState != SMANAGER_LIST)
                    break;
                LpmsU2::listDevices(&deviceList);
    #endif
    #ifdef BUILD_BLUETOOTH
                if (managerState != SMANAGER_LIST)
                    break;
                LpmsBBluetooth::listDevices(&deviceList);
    #endif
                managerState = SMANAGER_MEASURE;
                break;
            }
    
    #ifdef __GNUC__
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
    #endif
        }
    
    #ifdef _WIN32		
        ce.close();
        // be.close();
    #endif
    }
    

    在其声明的时候就new了一个线程进行run操作

    LpmsSensorManager::LpmsSensorManager(JavaVM *thisVm, jobject bluetoothAdapter) :
    thisVm(thisVm),
    bluetoothAdapter(bluetoothAdapter)
    #endif
    {
        stopped = false;
        isRecording = false;
        threadDelay = 500;
        currentUartBaudrate = SELECT_LPMS_UART_BAUDRATE_115200;
        verbose = true;
        managerState = SMANAGER_MEASURE;
    
        std::thread t(&LpmsSensorManager::run, this); //新建线程,执行run函数
    
    #ifdef _WIN32	
    #ifdef THREAD_HIGH_PRIORITY
        HANDLE th = t.native_handle();
        SetThreadPriority(th, THREAD_PRIORITY_HIGHEST);
    #endif
    #endif
    
        t.detach();
    #ifdef ANDROID
        LOGV("[LpmsSensorManager] Started");
    #endif
    }
    

    可见就是不断执行update进行数据的采集,update程序如下:

    ... ... 
    // Main measurement state
        case STATE_MEASURE:
            assertConnected();
    
            // Start next measurement step only if program is not waiting for data or ACK
            if (bt->isWaitForData() == false && bt->isWaitForAck() == false) {
                if (bt->getMode() != SELECT_LPMS_MODE_STREAM) {
                    bt->setStreamMode();
                    prepareStream = 0;
                    break;
                }
            }
    
            // TODO: Insert error handling for sensor.
            // if (bt->isError() == true) {
            // setSensorStatus(SENSOR_STATUS_ERROR);
            // }
    
            if (paused == true) {
                break;
            }
    
            if (prepareStream < STREAM_N_PREPARE) {
                ++prepareStream;
                break;
            }
    
            // Load current data from hardware and calculate rotation matrix and Euler angle
            if (bt->getLatestImuData(&imuData) == false) break; //可见是从imuDataQueue弹出imuData
    /*
    bool LpmsIoInterface::getLatestImuData(ImuData *id)
    {
        if (imuDataQueue.empty() == true) return false;
    
        *id = imuDataQueue.front();
        imuDataQueue.pop();
    
        return true;
    }
    */
            frameTime = lpmsTimer.measure() / 1000.0f;
            lpmsTimer.reset();
            setFps(frameTime);
    
            convertArrayToLpVector4f(imuData.q, &q);
            quaternionToMatrix(&q, &m);
            convertLpMatrixToArray(&m, imuData.rotationM);
    
            // Add frame number timestamp and IMU ID to current ImuData
            ++frameNo;
            imuData.frameCount = frameNo;
            imuData.openMatId = configData.openMatId;
    
            setConnectionStatus(SENSOR_CONNECTION_CONNECTED);
    
            if (isMagCalibrationEnabled == true) {
                setSensorStatus(SENSOR_STATUS_CALIBRATING);
            }
            else {
                if (paused == false) {
                    setSensorStatus(SENSOR_STATUS_RUNNING);
                }
                else {
                    setSensorStatus(SENSOR_STATUS_PAUSED);
                }
            }
    
            convertArrayToLpVector3f(imuData.aRaw, &aRaw);
            convertArrayToLpVector3f(imuData.bRaw, &bRaw);
            convertArrayToLpVector3f(imuData.gRaw, &gRaw);
    
            // Corrects magnetometer measurement
            if ((bt->getConfigReg() & LPMS_MAG_RAW_OUTPUT_ENABLED) != 0) {
                vectSub3x1(&bRaw, &configData.hardIronOffset, &b);
                matVectMult3(&configData.softIronMatrix, &b, &b);
            }
            else {
                vectZero3x1(&b);
            }
    
            // Corrects accelerometer measurement
            if ((bt->getConfigReg() & LPMS_ACC_RAW_OUTPUT_ENABLED) != 0) {
                matVectMult3(&configData.misalignMatrix, &aRaw, &a);
                vectAdd3x1(&configData.accBias, &a, &a);
            }
            else {
                vectZero3x1(&a);
            }
    
            // Corrects gyro measurement
            if ((bt->getConfigReg() & LPMS_GYR_RAW_OUTPUT_ENABLED) != 0) {
                matVectMult3(&configData.gyrMisalignMatrix, &gRaw, &g);
                vectAdd3x1(&configData.gyrAlignmentBias, &g, &g);
            }
            else {
                vectZero3x1(&g);
            }
    
            convertLpVector3fToArray(&a, imuData.a);
            convertLpVector3fToArray(&b, imuData.b);
            convertLpVector3fToArray(&g, imuData.g);
    
            // Checks, if calibration is active
            checkMagCal(frameTime);
            checkPlanarMagCal(frameTime);
            checkMisalignCal(frameTime);
            checkGyrMisalignCal(frameTime);
            checkMagMisalignCal(frameTime);
            checkMagReferenceCal(frameTime);
            
            // Sets current datac
            setCurrentData(imuData); //可见其实现是将数据压到dataQueue,当其长度小于dataQueueLength时。
    /*
    void LpmsSensor::setCurrentData(ImuData d)
    {
        std::unique_lock<std::mutex> lock(sensorMutex);
    
        currentData = d;
    
        if (dataQueue.size() < dataQueueLength) {
            dataQueue.push(d);
        }
        else {
            dataQueue.pop();
            dataQueue.push(d);
        }
    
        if (lpmsCallback) {
            lpmsCallback(d, deviceId.c_str());
        }
        newDataCondition.notify_one();
    }
    */
            // Checks, if data saving is active
            checkSaveData(); //检测save与否,并执行操作
            break;
    ... ...
    

    该程序中值得注意的有两个函数,一个函数是getLatestImuData 可见是从imuDataQueue弹出imuData。

    bool LpmsIoInterface::getLatestImuData(ImuData *id)
    {
        if (imuDataQueue.empty() == true) return false;
    
        *id = imuDataQueue.front();
        imuDataQueue.pop();
    
        return true;
    }
    

    一个函数是setCurrentData,可见其实现是将数据压到dataQueue,当其长度小于dataQueueLength时。

    void LpmsSensor::setCurrentData(ImuData d)
    {
        std::unique_lock<std::mutex> lock(sensorMutex);
    
        currentData = d;
    
        if (dataQueue.size() < dataQueueLength) {
            dataQueue.push(d);
        }
        else {
            dataQueue.pop();
            dataQueue.push(d);
        }
    
        if (lpmsCallback) {
            lpmsCallback(d, deviceId.c_str());
        }
        newDataCondition.notify_one();
    }
    

    然后查看我们使用的getCurrentData函数,其是从dataQueue弹出的数据,也就是说不需要跟传感器通信,我们只需要从dataQueue中获取数据即可,但是应该保证数据采集程序在数据采集周期将数据取出,如果不行的话,则会导致数据丢失,即自编上位机时不需要多线程进行数据采集,只使用while循环就可以完成数据采集,多线程反而导致电脑性能不足而导致数据丢失。

    ImuData LpmsSensor::getCurrentData(void)
    {
        ImuData d;
    
        bt->zeroImuData(&d);
    
        sensorMutex.lock();
    
        if (dataQueue.size() > 0) {
            d = dataQueue.front();
            dataQueue.pop();
        }
        else {
            d = currentData;
        }
    
        sensorMutex.unlock();
    
        return d;
    }
    

    对于imuDataQueue的获得是在蓝牙程序parseSensorData中实现的。

    bool LpmsBle::parseSensorData(void)
    {
    	unsigned o=0;
    	const float r2d = 57.2958f;
    	int iTimestamp;
    	int iQuat;
    	int iHeave;
    
    	zeroImuData(&imuData); 
    	
    	fromBufferInt16(oneTx, o, &iTimestamp);
    	o = o + 2;
    	currentTimestamp = (float) iTimestamp;
    	
    	if (timestampOffset > currentTimestamp) timestampOffset = currentTimestamp;
    	imuData.timeStamp = currentTimestamp - timestampOffset;
    	
    	fromBufferInt16(oneTx, o, &iQuat);
    	o = o + 2;
    	imuData.q[0] = (float) iQuat / (float) 0x7fff;
    	
    	fromBufferInt16(oneTx, o, &iQuat);
    	o = o + 2;
    	imuData.q[1] = (float) iQuat / (float) 0x7fff;
    
    	fromBufferInt16(oneTx, o, &iQuat);
    	o = o + 2;
    	imuData.q[2] = (float) iQuat / (float) 0x7fff;
    
    	fromBufferInt16(oneTx, o, &iQuat);
    	o = o + 2;
    	imuData.q[3] = (float) iQuat / (float) 0x7fff;
    	
    	fromBufferInt16(oneTx, o, &iHeave);
    	o = o + 2;
    	imuData.hm.yHeave = (float) iHeave / (float) 0x0fff;
    	
    	if (imuDataQueue.size() < 64) {
    		imuDataQueue.push(imuData);
    	}
    
    	return true;
    }
    

    其中parseSensorData在parseFunction中调用。

    bool LpmsIoInterface::parseFunction(void)
    {
         ... ...
    	case GET_SENSOR_DATA:
            parseSensorData();
            break;
         ... ...
    }
    
    

    parseFunction在函数parseModbusByte中调用。

    bool LpmsBBluetooth::parseModbusByte(void){ 
        ... ...
    		case PACKET_LRC_CHECK1:
                lrcReceived = lrcReceived + ((unsigned)b * 256);
                if (lrcReceived == lrcCheck) {
                    parseFunction();
                }
                else {
                    if (verbose) logd(TAG, "Checksum fail in data packet
    ");
                }
    
                rxState = PACKET_END;
                break;
         ... ...
    }
    

    parseModbusByte在checkState中调用。

    bool LpemgIoInterface::checkState(void)
    {
    	parseModbusByte();
        ... ...
    }
    

    checkState在pollData中调用。

    void LpmsSensor::pollData(void)
    {
        if (bt->deviceStarted() == true) {
            if (!bt->pollData())
                if (verbose) logd(TAG, "Poll Data error: %s
    ", bt->getErrorMsg().c_str());
    
            bt->checkState();
        }
    }
    

    可见pollData实现了从传感器获取数据,保存至imuDataQueue,而update实现了数据处理并将数据保存至dataQueue。

    下面是数据采集的子线程。

    bool IMUDAQ_Task::IMUDAQ()
    {
    	bool first = true;
    	timeb start, end;
    	int i[4] = { 0,0,0,0};
    	int j = 0;
    	while (1) {
    		ftime(&start);
    		ftime(&end);
    		while ((end.millitm - start.millitm + 1000 * (end.time - start.time) <= period * 1000 || stopbyuser || onceonly)
    			&& running)
    		{
    			j = 0;
    			for (auto lpms : Lpms) {
    				if (lpms->hasImuData() > 0) {
    					imudata = lpms->getCurrentData();
    					memcpy(Quaternion.data, imudata.q, 4 * sizeof(float));
    					scalarVectMult4x1(vect4x1Norm(Quaternion), &Quaternion, &QuaternionNormal);
    					memcpy(LinAcc.data, imudata.linAcc, 3 * sizeof(float));
    					quatRotVec(QuaternionNormal, LinAcc, &GlobalLinAcc);
    					if (!send) {
    						leg = Leg[j / 2];
    						legposition = Legposition[j % 2];
    					}
    					else {
    						signal.set_leg(ImuTutorial::Signal::Leg(j / 2));
    						signal.set_legposition(ImuTutorial::Signal::LegPosition(j % 2));
    					}
    					savedata();
    					i[j]++;
    				}
    				j++;
    			}
    			if (first)
    			{
    				first = false;
    				if (onceonly)
    					break;
    			}
    			ftime(&end);
    		}
    		if(end.millitm - start.millitm + 1000 * (end.time - start.time) > period*1000 || stopbyuser || onceonly)
    			processing = false;
    		if ((running == false)&&!first)
    		{
    			break;
    		}
    		if ((running == true) && ((processing == false)|| onceonly)) {
    			running = false;
    			break;
    		}
    	}
    	std::cout << std::endl;
    	for (int n = 0; n < 4; n++)
    		std::cout << Leg[n / 2] << " " << Legposition[n % 2] << " data lenght:" << i[n] << std::endl;
    	if (processing == false && !send) IMU_log.close();
    	t = nullptr;
    	for (auto lpms:Lpms)
    		lpms->pause();
    	std::cout << "Data Acquisition over !" << std::endl;
    	std::cout << "Please enter your command : ";
    	return(true);
    }
    

    流程图展示

    graph TB pollData-->checkState checkState-->parseModbusByte parseModbusByte-->parseFunction parseFunction-->parseSensorData parseSensorData-->imuDataQueueLength(if imuDataQueueLength > 64) imuDataQueueLength-->|yes push| imuDataQueue imuDataQueueLength-->|no| update imuDataQueue-->update update-->getLatestImuData getLatestImuData-->|pop| imuData imuData-->setCurrentData setCurrentData-->dataQueueLength(if dataQueueLength > 64) dataQueueLength-->|yes push| dataQueue dataQueueLength-->|no| pollData dataQueue-->pollData dataQueue-->|pop| getCurrentData getCurrentData-->|push| ImuSendimudataQueue ImuSendimudataQueue-->ImuSendimudataQueueEmpty(if all ImuSendimudataQueue is not empty) ImuSendimudataQueueEmpty-->|yes| PopImudata ImuSendimudataQueueEmpty-->|no|getCurrentData PopImudata-->|!send| ImuSave ImuSave-->getCurrentData PopImudata-->|send| ImuSend ImuSend-->getCurrentData
    任世事无常,勿忘初心
  • 相关阅读:
    C#获取HTML文件指定DIV内容
    剔除editor编辑器中的HTML标签
    ASP.NET MVC 在WebService中Token的使用方法
    MVC如何在单独的类库中添加区域
    原来写插件还可以选MEF
    EF CodeFirst学习
    .Net EF 学习之model first
    sql分页存储过程,带求和、排序
    软实力
    微软的XML可视化编辑器:XML Notepad 2007
  • 原文地址:https://www.cnblogs.com/FlameBlog/p/14715391.html
Copyright © 2011-2022 走看看