jjzjj

在Ubuntu20.04系统上LIO-SAM跑KITTI数据集和自己数据集代码修改

学无止境的小龟 2023-05-05 原文

LIO-SAM跑KITTI数据集和自己数据集代码修改


一、编译并运行LIO-SAM

参考我的另一篇文章:
Ubuntu20.04下的编译与运行LIO-SAM【问题解决】

二、代码修改

因为liosam 要求输入的点云每个点都有ring 信息和相对时间time信息,目前的雷达驱动基本具备这些信息,但是早期的KITTI数据集不具备,所以代码要自己计算一下 ring和time。方法可以参考lego-loam中这部分内容,具体修改如下。

1、cloud_info.msg

添加
# 用于改写ring和time相关
float32 startOrientation
float32 endOrientation
float32 orientationDiff

2、imageProjection.cpp

ring部分:

1、把ring通道强制关闭
2、添加计算ring代码
if (false){
	rowIdn = laserCloudIn->points[i].ring;
} else {
	verticalAngle = atan2(thisPoint.z, sqrt(thisPoint.x * thisPoint.x + thisPoint.y * thisPoint.y)) * 180 / M_PI;
	// 拿16、32、64线激光雷达为例
	if(N_SCAN == 16) {
		rowIdn = int((verticalAngle + 15) / 2 + 0.5);
		if (rowIdn < 0 || rowIdn >= N_SCAN)
		{
			continue;
		} else if(rowIdn % downsampleRate != 0) {
			continue;
		}
	} else if (N_SCAN == 32) {
    	rowIdn = int((verticalAngle + 92.0 / 3.0) * 3.0 / 4.0);
        if (rowIdn < 0 || rowIdn >= N_SCAN)
        {
            continue;
        } else if(rowIdn % downsampleRate != 0) {
            continue;
        }
    } else if (N_SCAN == 64) {
        if (verticalAngle >= -8.83) {
            rowIdn = int((2 - verticalAngle) * 3.0 + 0.5);
        } else {
            rowIdn = N_SCAN / 2 + int((-8.83 - verticalAngle) * 2.0 + 0.5);
        }
        // use [0 50]  > 50 remove outlies 
        if (verticalAngle > 2 || verticalAngle < -24.33 || rowIdn > 50 || rowIdn < 0)
        {
            continue;
        } else if(rowIdn % downsampleRate != 0) {
            continue;
        }
    } else {
        printf("wrong scan number\n");
        ROS_BREAK();
    }
}

time部分:

1、把time通道强制关闭
2、计算time并赋值
bool halfPassed = false;
int cloudNum = laserCloudIn->points.size();
// start and end orientation of this cloud
cloudInfo.startOrientation = -atan2(laserCloudIn->points[0].y, laserCloudIn->points[0].x);
cloudInfo.endOrientation   = -atan2(laserCloudIn->points[laserCloudIn->points.size() - 1].y, laserCloudIn->points[laserCloudIn->points.size() - 1].x) + 2 * M_PI;
if (cloudInfo.endOrientation - cloudInfo.startOrientation > 3 * M_PI) {
    cloudInfo.endOrientation -= 2 * M_PI;
} else if (cloudInfo.endOrientation - cloudInfo.startOrientation < M_PI) {
    cloudInfo.endOrientation += 2 * M_PI;
}
cloudInfo.orientationDiff = cloudInfo.endOrientation - cloudInfo.startOrientation;
PointType point;
for (int i = 0; i < cloudNum; ++i)
{
    point.x = laserCloudIn->points[i].x;
    point.y = laserCloudIn->points[i].y;
    point.z = laserCloudIn->points[i].z;
    float ori = -atan2(point.y, point.x);
    if (!halfPassed) {
        if (ori < cloudInfo.startOrientation - M_PI / 2) {
            ori += 2 * M_PI;
        } else if (ori > cloudInfo.startOrientation + M_PI * 3 / 2) {
            ori -= 2 * M_PI;
        }
        if (ori - cloudInfo.startOrientation > M_PI) {
            halfPassed = true;
        }
    } else {
        ori += 2 * M_PI;
        if (ori < cloudInfo.endOrientation - M_PI * 3 / 2) {
            ori += 2 * M_PI;
        } else if (ori > cloudInfo.endOrientation + M_PI / 2) {
            ori -= 2 * M_PI;
        }
    }
    float relTime = (ori - cloudInfo.startOrientation) / cloudInfo.orientationDiff;
    // 激光雷达10Hz,周期0.1
    laserCloudIn->points[i].time = 0.1 * relTime;
}

需要修改好的,可以查看我的

三、KITTI数据集准备

关于KITTI数据集,已有公开的kitti2bag工具,使用方法:参见我的另一个博客在Ubuntu20.04系统上将KITTI原始数据集转化为.bag形式。但是输出的bag文件liosam是不能正常跑的,位姿Z型突变,仔细了解一下发现这个bag的imu频率跟雷达一样,也就是很低频,无法满足代码需求。liosam作者提供了一个2011_09_30_drive_0028.bag在google drive,但可能无法快速下载。

如果想自己制作bag,作者自己改了kitti2bag就在源码的文件夹下,你需要准备如下文件(文件位置需对应):

首先,在终端输入以下指令:

pip3 install tqdm

效果:

然后,在"2011_10_03"文件夹的上一级目录(即:此处的2011_10_03_calib文件下),打开终端,输入:

python3 kitti2bag.py -t 2011_10_03 -r 0027 raw_synced

注意自己的文件的文件名

效果如下:

我第一次文件位置不对,导致无法生成bag文件

四、自己数据集准备

具体采集步骤在后续更新…

五、修改配置文件params.yaml

1、话题名修改

  # Topics
  pointCloudTopic: "points_raw"               # Point cloud data
  imuTopic: "imu_raw"                         # IMU data

2、根据KITTI采集数据的实际传感器修改对应参数

  # KITTI
  sensor: velodyne                            # lidar sensor type, either 'velodyne' or 'ouster'
  N_SCAN: 64                                  # number of lidar channel (i.e., 16, 32, 64, 128)
  Horizon_SCAN: 2083                          # lidar horizontal resolution (Velodyne:1800, Ouster:512,1024,2048)
  downsampleRate: 1                           # default: 1. Downsample your data if too many points. i.e., 16 = 64 / 4, 16 = 16 / 1
  lidarMinRange: 1.0                          # default: 1.0, minimum lidar range to be used
  lidarMaxRange: 1000.0                       # default: 1000.0, maximum lidar range to be used

对照我的另一个博客:LeGO-LOAM跑KITTI数据集评估方法【代码改】

3、外参的修改

  # kitti外参
  extrinsicTrans: [-8.086759e-01, 3.195559e-01, -7.997231e-01]
  extrinsicRot: [9.999976e-01, 7.553071e-04, -2.035826e-03,
                  -7.854027e-04, 9.998898e-01, -1.482298e-02,
                  2.024406e-03, 1.482454e-02, 9.998881e-01]
  extrinsicRPY: [9.999976e-01,  7.553071e-04, -2.035826e-03,
                 -7.854027e-04, 9.998898e-01, -1.482298e-02,
                  2.024406e-03, 1.482454e-02, 9.998881e-01]

注意点:
1、配置文件(params.yaml)内的参数通过参数服务器传送进源程序,会覆盖掉头文件内(utility.h)的对应参数。
2、其中extrinsicRot表示imu -> lidar的坐标变换,用于旋转imu坐标系下的角速度和线加速度到lidar坐标系下,extrinsicRPY则用于旋转imu坐标系下的欧拉角(姿态信息)到lidar坐标下,由于lio-sam作者使用的imu的欧拉角旋转指向与lidar坐标系不一致,因此使用了两个不同的旋转矩阵,但是大部分的设备两个旋转应该是设置为相同的。

六、GPS信息的添加

待更新…

七、效果图

KITTI:

00的可能会飞,05以后的应该没问题

八、轨迹保存

找到输出位姿信息,通过以下代码,输出位姿信息(KITTI格式):

// 位姿输出到txt文档
std::ofstream pose1("/home/cupido/output-data/lio-sam/pose.txt", std::ios::app);
pose1.setf(std::ios::scientific, std::ios::floatfield);
// pose1.precision(15);

//save final trajectory in the left camera coordinate system.
Eigen::Matrix3d rotation_matrix;
rotation_matrix = Eigen::AngleAxisd(pose_in.yaw, Eigen::Vector3d::UnitZ()) * 
                  Eigen::AngleAxisd(pose_in.pitch, Eigen::Vector3d::UnitY()) * 
                  Eigen::AngleAxisd(pose_in.roll, Eigen::Vector3d::UnitX());
Eigen::Matrix<double, 4, 4> mylio_pose;
mylio_pose.topLeftCorner(3,3) = rotation_matrix;

mylio_pose(0,3) = pose_in.x;
mylio_pose(1,3) = pose_in.y;
mylio_pose(2,3) = pose_in.z;
Eigen::Matrix<double, 4, 4> cali_paremeter;
/*cali_paremeter << 4.276802385584e-04, -9.999672484946e-01, -8.084491683471e-03, -1.198459927713e-02,  //00-02
                   -7.210626507497e-03, 8.081198471645e-03, -9.999413164504e-01, -5.403984729748e-02, 
                    9.999738645903e-01, 4.859485810390e-04, -7.206933692422e-03, -2.921968648686e-01,
                    0,                    0,                   0,                          1;*/
/*cali_paremeter << -1.857739385241e-03,-9.999659513510e-01, -8.039975204516e-03, -4.784029760483e-03,
                    -6.481465826011e-03, 8.051860151134e-03, -9.999466081774e-01, -7.337429464231e-02,
                     9.999773098287e-01, -1.805528627661e-03, -6.496203536139e-03, -3.339968064433e-01,     //04-10
                     0                    0,                   0,                          1;*/
cali_paremeter << 2.347736981471e-04, -9.999441545438e-01, -1.056347781105e-02, -2.796816941295e-03,
                  1.044940741659e-02, 1.056535364138e-02, -9.998895741176e-01, -7.510879138296e-02, 
                  9.999453885620e-01, 1.243653783865e-04, 1.045130299567e-02, -2.721327964059e-01,
                  0,                     0,                   0,                          1;
         
Eigen::Matrix<double, 4, 4> myloam_pose_f;
myloam_pose_f = cali_paremeter * mylio_pose * cali_paremeter.inverse();

pose1 << myloam_pose_f(0,0) << " " << myloam_pose_f(0,1) << " " << myloam_pose_f(0,2) << " " << myloam_pose_f(0,3) << " "
      << myloam_pose_f(1,0) << " " << myloam_pose_f(1,1) << " " << myloam_pose_f(1,2) << " " << myloam_pose_f(1,3) << " "
      << myloam_pose_f(2,0) << " " << myloam_pose_f(2,1) << " " << myloam_pose_f(2,2) << " " << myloam_pose_f(2,3) << std::endl;

pose1.close();

欧拉角到四元素的转换除了用eigen,还可以参考大佬:四元数与欧拉角(Yaw、Pitch、Roll)的转换

补充tum格式的轨迹输出(拿ALOAM举例,LIO-SAM修改相关参数即可)

// 位姿输出到txt文档
std::ofstream pose1("/home/cupido/output-data/kitti/sequences/07/aloam.txt", std::ios::app);
pose1.setf(std::ios::scientific, std::ios::floatfield);
pose1.precision(15);

//save final trajectory in the left camera coordinate system.
Eigen::Matrix3d rotation_matrix;
rotation_matrix = q_w_curr.toRotationMatrix();
Eigen::Matrix<double, 4, 4> myaloam_pose;
myaloam_pose.topLeftCorner(3,3) = rotation_matrix;

myaloam_pose(0,3) = t_w_curr.x();
myaloam_pose(1,3) = t_w_curr.y();
myaloam_pose(2,3) = t_w_curr.z();
Eigen::Matrix<double, 4, 4> cali_paremeter;
// cali_paremeter << 4.276802385584e-04, -9.999672484946e-01, -8.084491683471e-03, -1.198459927713e-02,  //00-02
//                	 -7.210626507497e-03, 8.081198471645e-03, -9.999413164504e-01, -5.403984729748e-02, 
//                   9.999738645903e-01, 4.859485810390e-04, -7.206933692422e-03, -2.921968648686e-01,
//                   0,                    0,                   0,                          1;
cali_paremeter << -1.857739385241e-03,-9.999659513510e-01, -8.039975204516e-03, -4.784029760483e-03,     //04-10
                  -6.481465826011e-03, 8.051860151134e-03, -9.999466081774e-01, -7.337429464231e-02,
                   9.999773098287e-01, -1.805528627661e-03, -6.496203536139e-03, -3.339968064433e-01,
                   0,                   0,                   0,                          1;
/*cali_paremeter << 2.347736981471e-04, -9.999441545438e-01, -1.056347781105e-02, -2.796816941295e-03,    // 03
                    1.044940741659e-02, 1.056535364138e-02, -9.998895741176e-01, -7.510879138296e-02, 
                    9.999453885620e-01, 1.243653783865e-04, 1.045130299567e-02, -2.721327964059e-01,
                    0,                     0,                   0,                          1;*/
         
Eigen::Matrix<double, 4, 4> myloam_pose_f;
myloam_pose_f = cali_paremeter * myaloam_pose * cali_paremeter.inverse();

Eigen::Matrix3d temp;
temp = myloam_pose_f.topLeftCorner(3,3);
Eigen::Quaterniond quaternion(temp);

// 获取当前更新的时间 这样与ground turth对比才更准确
static double timeStart = odometryBuf.front()-> header.stamp.toSec();	// 相当于是第一帧的时间
auto T1 = ros::Time().fromSec(timeStart);
pose1 << odomAftMapped.header.stamp - T1  << " "
      << myloam_pose_f(0,3) << " "
      << myloam_pose_f(1,3) << " "
      << myloam_pose_f(2,3) << " "
      << quaternion.x() << " "
      << quaternion.y() << " "
      << quaternion.z() << " "
      << quaternion.w() << std::endl;

pose1.close();

注意点:
1、输出路径注意修改
2、评估轨迹精度的时候,输出轨迹若发现未和真值完全对齐(这里指的是,不考虑自己算法精度,单纯两轨迹对齐),可以在终端输入以下指令:

//(注意是-a,不是--align_origin)
evo_ape tum groundtruth.txt xxx.txt -a -p

九、点云地图保存(PCD)

追根溯源

1、注意到save_map.srv文件

float32 resolution
string destination
---
bool success

2、进入到mapOptmization.cpp

相关代码:

// 订阅一个保存地图功能的服务
srvSaveMap  = nh.advertiseService("lio_sam/save_map", &mapOptimization::saveMapService, this);

/**
 * 保存全局关键帧特征点集合
 */
bool saveMapService(lio_sam::save_mapRequest& req, lio_sam::save_mapResponse& res)
{
  string saveMapDirectory;

  cout << "****************************************************" << endl;
  cout << "Saving map to pcd files ..." << endl;
  // 如果是空,说明是程序结束后的自动保存,否则是中途调用ros的service发布的保存指令
  if(req.destination.empty()) saveMapDirectory = std::getenv("HOME") + savePCDDirectory;
  else saveMapDirectory = std::getenv("HOME") + req.destination;
  cout << "Save destination: " << saveMapDirectory << endl;
  // create directory and remove old files;
  // 删掉之前有可能保存过的地图
  int unused = system((std::string("exec rm -r ") + saveMapDirectory).c_str());
  unused = system((std::string("mkdir -p ") + saveMapDirectory).c_str());
  // save key frame transformations
  // 首先保存历史关键帧轨迹
  pcl::io::savePCDFileBinary(saveMapDirectory + "/trajectory.pcd", *cloudKeyPoses3D);
  pcl::io::savePCDFileBinary(saveMapDirectory + "/transformations.pcd", *cloudKeyPoses6D);
  // extract global point cloud map(提取历史关键帧角点、平面点集合)
  pcl::PointCloud<PointType>::Ptr globalCornerCloud(new pcl::PointCloud<PointType>());
  pcl::PointCloud<PointType>::Ptr globalCornerCloudDS(new pcl::PointCloud<PointType>());
  pcl::PointCloud<PointType>::Ptr globalSurfCloud(new pcl::PointCloud<PointType>());
  pcl::PointCloud<PointType>::Ptr globalSurfCloudDS(new pcl::PointCloud<PointType>());
  pcl::PointCloud<PointType>::Ptr globalMapCloud(new pcl::PointCloud<PointType>());
  // 遍历所有关键帧,将点云全部转移到世界坐标系下去
  for (int i = 0; i < (int)cloudKeyPoses3D->size(); i++) {
      *globalCornerCloud += *transformPointCloud(cornerCloudKeyFrames[i],  &cloudKeyPoses6D->points[i]);
      *globalSurfCloud   += *transformPointCloud(surfCloudKeyFrames[i],    &cloudKeyPoses6D->points[i]);
      // 类似进度条的功能
      cout << "\r" << std::flush << "Processing feature cloud " << i << " of " << cloudKeyPoses6D->size() << " ...";
  }
  // 如果没有指定分辨率,就是直接保存
  if(req.resolution != 0)
  {
    cout << "\n\nSave resolution: " << req.resolution << endl;

    // down-sample and save corner cloud
    // 使用指定分辨率降采样,分别保存角点地图和面点地图
    downSizeFilterCorner.setInputCloud(globalCornerCloud);
    downSizeFilterCorner.setLeafSize(req.resolution, req.resolution, req.resolution);
    downSizeFilterCorner.filter(*globalCornerCloudDS);
    pcl::io::savePCDFileBinary(saveMapDirectory + "/CornerMap.pcd", *globalCornerCloudDS);
    // down-sample and save surf cloud
    downSizeFilterSurf.setInputCloud(globalSurfCloud);
    downSizeFilterSurf.setLeafSize(req.resolution, req.resolution, req.resolution);
    downSizeFilterSurf.filter(*globalSurfCloudDS);
    pcl::io::savePCDFileBinary(saveMapDirectory + "/SurfMap.pcd", *globalSurfCloudDS);
  }
  else
  {
    // save corner cloud
    pcl::io::savePCDFileBinary(saveMapDirectory + "/CornerMap.pcd", *globalCornerCloud);
    // save surf cloud
    pcl::io::savePCDFileBinary(saveMapDirectory + "/SurfMap.pcd", *globalSurfCloud);
  }

  // save global point cloud map(保存到一起,全局关键帧特征点集合)
  *globalMapCloud += *globalCornerCloud;
  *globalMapCloud += *globalSurfCloud;
  // 保存全局地图
  int ret = pcl::io::savePCDFileBinary(saveMapDirectory + "/GlobalMap.pcd", *globalMapCloud);
  res.success = ret == 0;

  downSizeFilterCorner.setLeafSize(mappingCornerLeafSize, mappingCornerLeafSize, mappingCornerLeafSize);
  downSizeFilterSurf.setLeafSize(mappingSurfLeafSize, mappingSurfLeafSize, mappingSurfLeafSize);

  cout << "****************************************************" << endl;
  cout << "Saving map to pcd files completed\n" << endl;

  return true;
}
std::thread visualizeMapThread(&mapOptimization::visualizeGlobalMapThread, &MO);   // 可视化的线程(负责rviz相关可视化接口的发布)

visualizeMapThread.join();

	/**
     * 展示线程
     * 1、发布局部关键帧map的特征点云
     * 2、保存全局关键帧特征点集合
    */
    // 全局可视化线程
    void visualizeGlobalMapThread()
    {
        // 更新频率设置为0.2hz
        ros::Rate rate(0.2);
        while (ros::ok()){
            rate.sleep();
            // 发布局部关键帧map的特征点云
            publishGlobalMap();
        }
        // 当ros被杀死之后,执行保存地图功能
        if (savePCD == false)
            return;

        lio_sam::save_mapRequest  req;
        lio_sam::save_mapResponse res;

        // 保存全局关键帧特征点集合
        if(!saveMapService(req, res)){
            cout << "Fail to save map" << endl;
        }
    }

这里注意到 if (savePCD == false)判断条件,转至配置文件params.yaml

3、最后在配置文件params.yaml修改参数

打开开关:

savePCD: true                                                   # https://github.com/TixiaoShan/LIO-SAM/issues/3

更改路径:

savePCDDirectory: "/output-data/lio-sam/PCD/"        # in your home folder, starts and ends with "/". Warning: the code deletes "LOAM" folder then recreates it. See "mapOptimization" for implementation

对应路径和自身电脑的全局路径的关系如下:

4、PCD效果展示

1、指令:

pcl_viewer xxx.pcd

2、效果图:

此节参考大佬:
1、lio-sam中点云地图保存
2、复现lio_sam激光slam算法创建点云地图
3、PCL保存LIO-SAM地图时报错[pcl::PCDWriter::writeBinary]

全文参考文献

1、ubuntu18运行编译LIO-SAM并用官网和自己的数据建图(修改汇总)
2、LIO-SAM运行自己数据包遇到的问题解决–SLAM不学无数术小问题
3、使用开源激光SLAM方案LIO-SAM运行KITTI数据集,如有用,请评论雷锋
4、LIO-SAM:配置环境、安装测试、适配⾃⼰采集数据集
5、SLAM学习笔记(十九)开源3D激光SLAM总结大全——Cartographer3D,LOAM,Lego-LOAM,LIO-SAM,LVI-SAM,Livox-LOAM的原理解析及区别
6、多传感器融合定位 第六章 惯性导航结算及误差模型

有关在Ubuntu20.04系统上LIO-SAM跑KITTI数据集和自己数据集代码修改的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  3. ruby - 如何在 Ubuntu 中清除 Ruby Phusion Passenger 的缓存? - 2

    我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:

  4. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  5. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  6. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  7. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  8. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  9. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  10. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

随机推荐