VINS-Mono源码分析7— pose_graph2(四自由度位姿图优化)

2023-05-16

VINS-Mono源码分析7— pose_graph2


在上一篇博文中,大概分析了一下VINS-Mono回环检测和重定位的代码实现,这里主要分析 四自由度的位姿图优化。关于这部分的原理可以参考VINS-Mono论文第8部分的A、B和C以及崔神的文章《VINS论文推导及代码解析》中的7.4节。接下来开始进行代码分析,

PoseGraph::PoseGraph()
{
    ......
	t_optimization = std::thread(&PoseGraph::optimize4DoF, this);
    ......
}

在pose_graph_node.cpp中,定义posegraph变量时,就开启了t_optimization线程,在这个线程中执行optimize4DoF函数

void PoseGraph::optimize4DoF()
{
    while(true)
    {
        int cur_index = -1;
        int first_looped_index = -1;
        m_optimize_buf.lock();
        while(!optimize_buf.empty())
        {
            //得到 和回环帧匹配成功的 当前关键帧的索引,
            //即optimize_buf的最后一个元素,因为如果不遍历到最后,跳不出循环
            cur_index = optimize_buf.front();
            //得到时间上最早的回环帧的索引,它和上面的cur_index是匹配的
            first_looped_index = earliest_loop_index;
            optimize_buf.pop();
        }
        m_optimize_buf.unlock();
        if (cur_index != -1)
        {
            printf("optimize pose graph \n");
            TicToc tmp_t;
            m_keyframelist.lock();
            //得到cur_index索引对应的关键帧
            KeyFrame* cur_kf = getKeyFrame(cur_index);
            //等待优化的关键帧的帧数上限,极端情况下优化所有关键帧
            int max_length = cur_index + 1;

            // 定义ceres库优化时会用到的变量
            double t_array[max_length][3];
            Quaterniond q_array[max_length];
            double euler_array[max_length][3];
            double sequence_array[max_length];
            //构建ceres问题,ceres库优化前的一些参数配置
            ceres::Problem problem;
            ceres::Solver::Options options;
            options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
            //options.minimizer_progress_to_stdout = true;
            //options.max_solver_time_in_seconds = SOLVER_TIME * 3;
            options.max_num_iterations = 5;
            ceres::Solver::Summary summary;
            ceres::LossFunction *loss_function;
            loss_function = new ceres::HuberLoss(0.1);
            //loss_function = new ceres::CauchyLoss(1.0);
            //AngleLocalParameterization类指定yaw角优化变量是如何进行迭代更新的
            ceres::LocalParameterization* angle_local_parameterization =
                AngleLocalParameterization::Create();

            list<KeyFrame*>::iterator it;

            int i = 0;
            for (it = keyframelist.begin(); it != keyframelist.end(); it++)
            {
                //回环帧之前的关键帧不优化,跳过
                if ((*it)->index < first_looped_index)
                    continue;
                //待优化关键帧的局部索引
                (*it)->local_index = i;
                //给ceres优化时用到的变量赋值
                Quaterniond tmp_q;
                Matrix3d tmp_r;
                Vector3d tmp_t;
                //注意getVioPose()函数与getPose()函数的区别
                (*it)->getVioPose(tmp_t, tmp_r);
                tmp_q = tmp_r;
                t_array[i][0] = tmp_t(0);
                t_array[i][1] = tmp_t(1);
                t_array[i][2] = tmp_t(2);
                q_array[i] = tmp_q;

                Vector3d euler_angle = Utility::R2ypr(tmp_q.toRotationMatrix());
                euler_array[i][0] = euler_angle.x();
                euler_array[i][1] = euler_angle.y();
                euler_array[i][2] = euler_angle.z();

                sequence_array[i] = (*it)->sequence;
                //添加参数块
                problem.AddParameterBlock(euler_array[i], 1, angle_local_parameterization);
                problem.AddParameterBlock(t_array[i], 3);

                if ((*it)->index == first_looped_index || (*it)->sequence == 0)
                {   //固定回环帧的欧拉角和位置
                    problem.SetParameterBlockConstant(euler_array[i]);
                    problem.SetParameterBlockConstant(t_array[i]);
                }

                //add edge
                //添加序列边的目的就是缩小回环帧-当前帧中间的那些关键帧的位姿误差
                for (int j = 1; j < 5; j++)
                {
                  if (i - j >= 0 && sequence_array[i] == sequence_array[i-j])
                  {
                    Vector3d euler_conncected = Utility::R2ypr(q_array[i-j].toRotationMatrix());
                    Vector3d relative_t(t_array[i][0] - t_array[i-j][0], t_array[i][1] - t_array[i-j][1], t_array[i][2] - t_array[i-j][2]);
                    relative_t = q_array[i-j].inverse() * relative_t;
                    double relative_yaw = euler_array[i][0] - euler_array[i-j][0];
                    ceres::CostFunction* cost_function = FourDOFError::Create( relative_t.x(), relative_t.y(), relative_t.z(),
                                                   relative_yaw, euler_conncected.y(), euler_conncected.z());
                    problem.AddResidualBlock(cost_function, NULL, euler_array[i-j], 
                                            t_array[i-j], 
                                            euler_array[i], 
                                            t_array[i]);
                  }
                }

                //add loop edge
                //注意四自由度位姿图优化的核心就在回环边残差块这里
                //如果这里的代码注释掉,只留下序列边残差块,那么优化和不优化没有区别
                if((*it)->has_loop)
                {
                    assert((*it)->loop_index >= first_looped_index);
                    int connected_index = getKeyFrame((*it)->loop_index)->local_index;
                    Vector3d euler_conncected = Utility::R2ypr(q_array[connected_index].toRotationMatrix());
                    Vector3d relative_t;
                    relative_t = (*it)->getLoopRelativeT();
                    double relative_yaw = (*it)->getLoopRelativeYaw();
                    ceres::CostFunction* cost_function = FourDOFWeightError::Create( relative_t.x(), relative_t.y(), relative_t.z(),
                                                                               relative_yaw, euler_conncected.y(), euler_conncected.z());
                    problem.AddResidualBlock(cost_function, loss_function, euler_array[connected_index], 
                                                                  t_array[connected_index], 
                                                                  euler_array[i], 
                                                                  t_array[i]);
                    
                }
                
                if ((*it)->index == cur_index)
                    break;
                i++;
            }
            m_keyframelist.unlock();
            //开始进行ceres优化
            ceres::Solve(options, &problem, &summary);
          
            m_keyframelist.lock();
            i = 0;
            for (it = keyframelist.begin(); it != keyframelist.end(); it++)
            {
                //回环帧之前的关键帧没有参与优化,跳过
                if ((*it)->index < first_looped_index)
                    continue;
                //得到优化后的位姿
                Quaterniond tmp_q;
                tmp_q = Utility::ypr2R(Vector3d(euler_array[i][0], euler_array[i][1], euler_array[i][2]));
                Vector3d tmp_t = Vector3d(t_array[i][0], t_array[i][1], t_array[i][2]);
                Matrix3d tmp_r = tmp_q.toRotationMatrix();
                //用优化后的位姿更新关键帧的位姿
                (*it)-> updatePose(tmp_t, tmp_r);
                //更新位姿到当前关键帧后,跳出循环
                if ((*it)->index == cur_index)
                    break;
                i++;
            }
            //得到四自由度优化前后的当前关键帧的的航向角误差yaw_drift和位置误差t_drift
            Vector3d cur_t, vio_t;
            Matrix3d cur_r, vio_r;
            cur_kf->getPose(cur_t, cur_r);
            cur_kf->getVioPose(vio_t, vio_r);
            m_drift.lock();
            yaw_drift = Utility::R2ypr(cur_r).x() - Utility::R2ypr(vio_r).x();
            r_drift = Utility::ypr2R(Vector3d(yaw_drift, 0, 0));
            t_drift = cur_t - r_drift * vio_t;
            m_drift.unlock();
            //当前关键帧后边的关键帧也调整一下位姿, 这是一个保险的操作,
            //防止关键帧链表中有比当前关键帧 更超前的关键帧存在。     
            it++;
            for (; it != keyframelist.end(); it++)
            {
                Vector3d P;
                Matrix3d R;
                (*it)->getVioPose(P, R);
                P = r_drift * P + t_drift;
                R = r_drift * R;
                (*it)->updatePose(P, R);
            }
            m_keyframelist.unlock();
            updatePath();
        }

        std::chrono::milliseconds dura(2000);
        std::this_thread::sleep_for(dura);
    }
}

下面是序列边残差计算的代码

struct FourDOFError
{
	FourDOFError(double t_x, double t_y, double t_z, double relative_yaw, double pitch_i, double roll_i)
				  :t_x(t_x), t_y(t_y), t_z(t_z), relative_yaw(relative_yaw), pitch_i(pitch_i), roll_i(roll_i){}

	template <typename T>
	bool operator()(const T* const yaw_i, const T* ti, const T* yaw_j, const T* tj, T* residuals) const
	{
		T t_w_ij[3];
		t_w_ij[0] = tj[0] - ti[0];
		t_w_ij[1] = tj[1] - ti[1];
		t_w_ij[2] = tj[2] - ti[2];

		// euler to rotation
		T w_R_i[9];
		YawPitchRollToRotationMatrix(yaw_i[0], T(pitch_i), T(roll_i), w_R_i);
		// rotation transpose
		T i_R_w[9];
		RotationMatrixTranspose(w_R_i, i_R_w);
		// rotation matrix rotate point
		T t_i_ij[3];
		RotationMatrixRotatePoint(i_R_w, t_w_ij, t_i_ij);

		residuals[0] = (t_i_ij[0] - T(t_x));
		residuals[1] = (t_i_ij[1] - T(t_y));
		residuals[2] = (t_i_ij[2] - T(t_z));
		residuals[3] = NormalizeAngle(yaw_j[0] - yaw_i[0] - T(relative_yaw));

		return true;
	}

	static ceres::CostFunction* Create(const double t_x, const double t_y, const double t_z,
									   const double relative_yaw, const double pitch_i, const double roll_i) 
	{
	  return (new ceres::AutoDiffCostFunction<
	          FourDOFError, 4, 1, 3, 1, 3>(
	          	new FourDOFError(t_x, t_y, t_z, relative_yaw, pitch_i, roll_i)));
	}

	double t_x, t_y, t_z;
	double relative_yaw, pitch_i, roll_i;

};

下面是回环边残差计算的代码

struct FourDOFWeightError
{
	FourDOFWeightError(double t_x, double t_y, double t_z, double relative_yaw, double pitch_i, double roll_i)
				  :t_x(t_x), t_y(t_y), t_z(t_z), relative_yaw(relative_yaw), pitch_i(pitch_i), roll_i(roll_i){
				  	weight = 1;
				  }

	template <typename T>
	bool operator()(const T* const yaw_i, const T* ti, const T* yaw_j, const T* tj, T* residuals) const
	{
		T t_w_ij[3];
		t_w_ij[0] = tj[0] - ti[0];
		t_w_ij[1] = tj[1] - ti[1];
		t_w_ij[2] = tj[2] - ti[2];

		// euler to rotation
		T w_R_i[9];
		YawPitchRollToRotationMatrix(yaw_i[0], T(pitch_i), T(roll_i), w_R_i);
		// rotation transpose
		T i_R_w[9];
		RotationMatrixTranspose(w_R_i, i_R_w);
		// rotation matrix rotate point
		T t_i_ij[3];
		RotationMatrixRotatePoint(i_R_w, t_w_ij, t_i_ij);

		residuals[0] = (t_i_ij[0] - T(t_x)) * T(weight);
		residuals[1] = (t_i_ij[1] - T(t_y)) * T(weight);
		residuals[2] = (t_i_ij[2] - T(t_z)) * T(weight);
		residuals[3] = NormalizeAngle((yaw_j[0] - yaw_i[0] - T(relative_yaw))) * T(weight) / T(10.0);
                std::cout<<"回环边"<<"residuals[3]"<<std::endl;
		return true;
	}

	static ceres::CostFunction* Create(const double t_x, const double t_y, const double t_z,
									   const double relative_yaw, const double pitch_i, const double roll_i) 
	{
	  return (new ceres::AutoDiffCostFunction<
	          FourDOFWeightError, 4, 1, 3, 1, 3>(
	          	new FourDOFWeightError(t_x, t_y, t_z, relative_yaw, pitch_i, roll_i)));
	}

	double t_x, t_y, t_z;
	double relative_yaw, pitch_i, roll_i;
	double weight;

};
void PoseGraph::updatePath()
{
    m_keyframelist.lock();
    list<KeyFrame*>::iterator it;
    //清空各图像序列的ROS位姿消息(默认是绿线)
    for (int i = 1; i <= sequence_cnt; i++)
    {
        path[i].poses.clear();
    }
    base_path.poses.clear();
    //清空RVIZ显示位姿的连线(默认是红线)
    posegraph_visualization->reset();
   //SAVE_LOOP_PATH=true,这里的代码貌似没有卵用
    if (SAVE_LOOP_PATH)
    {
        //获取文件流
        ofstream loop_path_file_tmp(VINS_RESULT_PATH, ios::out);
        loop_path_file_tmp.close();
    }

    for (it = keyframelist.begin(); it != keyframelist.end(); it++)
    {
        //得到关键帧列表中关键帧的位姿
        Vector3d P;
        Matrix3d R;
        (*it)->getPose(P, R);
        Quaterniond Q;
        Q = R;
        //构造ROS位姿消息pose_stamped        
        geometry_msgs::PoseStamped pose_stamped;
        pose_stamped.header.stamp = ros::Time((*it)->time_stamp);
        pose_stamped.header.frame_id = "world";
        pose_stamped.pose.position.x = P.x() + VISUALIZATION_SHIFT_X;
        pose_stamped.pose.position.y = P.y() + VISUALIZATION_SHIFT_Y;
        pose_stamped.pose.position.z = P.z();
        pose_stamped.pose.orientation.x = Q.x();
        pose_stamped.pose.orientation.y = Q.y();
        pose_stamped.pose.orientation.z = Q.z();
        pose_stamped.pose.orientation.w = Q.w();
        //将ROS位姿消息添加到相应序列中
        if((*it)->sequence == 0)
        {
            base_path.poses.push_back(pose_stamped);
            base_path.header = pose_stamped.header;
        }
        else
        {
            path[(*it)->sequence].poses.push_back(pose_stamped);
            path[(*it)->sequence].header = pose_stamped.header;
        }
         //SAVE_LOOP_PATH=true
        if (SAVE_LOOP_PATH)
        {
            //获取文件流,在文件末尾继续添加
            ofstream loop_path_file(VINS_RESULT_PATH, ios::app);
            loop_path_file.setf(ios::fixed, ios::floatfield);
            //浮点数精度控制
            loop_path_file.precision(0);
            loop_path_file << (*it)->time_stamp * 1e9 << ",";
            loop_path_file.precision(5);
            loop_path_file  << P.x() << ","
                  << P.y() << ","
                  << P.z() << ","
                  << Q.w() << ","
                  << Q.x() << ","
                  << Q.y() << ","
                  << Q.z() << ","
                  << endl;
            loop_path_file.close();
        }
        //画序列边,it关键帧和它前边四个关键帧的连接,默认不执行
        if (SHOW_S_EDGE)
        {
            ......
        }
         //画回环边,回环帧和 与它匹配的关键帧的连接
        if (SHOW_L_EDGE)
        {
            if ((*it)->has_loop && (*it)->sequence == sequence_cnt)
            {
                
                KeyFrame* connected_KF = getKeyFrame((*it)->loop_index);
                Vector3d connected_P;
                Matrix3d connected_R;
                connected_KF->getPose(connected_P, connected_R);
                //(*it)->getVioPose(P, R);
                (*it)->getPose(P, R);
                if((*it)->sequence > 0)
                {
                    posegraph_visualization->add_loopedge(P, connected_P + Vector3d(VISUALIZATION_SHIFT_X, VISUALIZATION_SHIFT_Y, 0));
                }
            }
        }

    }
    //发布各序列的位姿消息,包括序列边和回环边连线,如果SHOW_S_EDGE=true的话。
    publish();
    m_keyframelist.unlock();
}
void PoseGraph::publish()
{
    for (int i = 1; i <= sequence_cnt; i++)
    {
        if (1 || i == base_sequence)
        {
            pub_pg_path.publish(path[i]);
            pub_path[i].publish(path[i]);
            posegraph_visualization->publish_by(pub_pose_graph, path[sequence_cnt].header);
        }
    }
    base_path.header.frame_id = "world";
    pub_base_path.publish(base_path);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VINS-Mono源码分析7— pose_graph2(四自由度位姿图优化) 的相关文章

  • matlab GUI学习笔记6 如何更改窗口标题,以及设置edit/text输入框的默认值

    写了好久的本子 xff0c 老师又给软件提了些要求 xff0c 需要进行修改 xff08 1 xff09 更改窗口标题 为了方便窗口编程 xff0c 每个窗口的名称都设置的很明了 xff0c 例如下图这样 xff0c 这个窗口对应的就是主窗
  • matlab cody学习笔记 day23 判断输入的是否是向量

    好久没更新了 xff0c 今天刷一道 1 Problem 605 Whether the input is vector Given the input x return 1 if x is vector or else 0 我本来想的是获
  • 串口通信校验方式(even,odd,space,mark)

    无校验 xff08 no parity xff09 奇校验 xff08 odd parity xff09 xff1a 如果字符数据位中 34 1 34 的数目是偶数 xff0c 校验位为 34 1 34 xff0c 如果 34 1 34 的
  • Eigen介绍及简单使用

    Eigen是可以用来进行线性代数 矩阵 向量操作等运算的C 43 43 库 xff0c 它里面包含了很多算法 它的License是MPL2 它支持多平台 Eigen采用源码的方式提供给用户使用 xff0c 在使用时只需要包含Eigen的头文
  • APM最新固件(20181220)

    ardupilot Makefile MAKEFILE LISTWAF BINARY 61 modules waf waf lightWAF 61 python WAF BINARY WAF FLAGSEXPLICIT COMMANDS 6
  • Java考试复习

    java考试复习 1 判断题 单选题 填空题看网上测试 xff1b 注意 xff01 xff01 xff01 xff08 单选题答案里面的粗黑的有分号是代表同时满足 xff1b 填空题答案里面的粗黑的有分号是代表其中一个就满足 xff09
  • 计算机网络之第4章 网络层

    计算机网络 第4章 网络层 网络层概述 以下属于网络层范畴的是 IP地址在因特网使用的TCP IP协议栈中 xff0c 核心协议是 IP 网络层提供的两种服务 TCP IP参考模型的网络层提供的是 无连接不可靠的数据报服务 IPv4地址概述
  • 中标麒麟Linux服务器5.0(mips64el)安装QT开发环境

    中标麒麟服务器5 0 xff08 mips64el xff09 上QT开发需要用到can口 xff0c 原版自带的QT不包含相关模块 xff0c 故重新下载一个带有can模块的qt版本进行安装 该系统架构是mips64el的 xff0c 所
  • mavros永远连接失败

    之前在做无人机使用px4源码避障的实物实验 xff0c 已经有了无人机 xff0c 想按照惯例先在nvidia NX上位机上跑一下仿真实验测试一下 xff0c 结果mavros用了一万种方法 xff0c 就是连不上 xff0c 仿真跑不了
  • 成功解决mingw下载太慢的问题

    MinGW w64 for 32 and 64 bit Windows Browse Files at SourceForge net 1 在此页面下滑找到你要下载的文件 2 点击Problems Downloadings 3 选择一个合适
  • PyQt5学习笔记9_使用setStyle和setStyleSheet进行换肤

    通过QStyleFactory keys 可获取用于setStyle的有效值 xff0c 本例程中包含 Windows xff0c WindowsXP xff0c WindowsVista xff0c Fusion 四种风格 xff0c 此
  • mkdir 创建目录

    参数选项 参数说明 p 连续创建目录 mkdir data 创建目录data 或 cd mkdir data 或 cd mkdir data 注 xff1a 是将两条命令分隔开 mkdir p data b c 连续创建目录 data b
  • CC, TBD, EOD都是什么鬼?拯救一写英文邮件就发慌

    职场新人在工作中经常听到这样的对话 xff1a 给客户的邮件记得CC我 xff0c BCC给财务 xff0c 告诉客户合同签订时间还TBD But CC BCC TBD到底是什么鬼 xff1f 马上来恶补一下职场英文缩写 xff0c 拯救一
  • Apache Openmeetings安装介绍

    翻译自Apache OpenMeetings 更新时间 xff1a 2017 01 11 目录 目录Openmeetings安装端口NAT设置自定义硬件需求Debian链接更新日志VoIP提示和技巧 Openmeetings安装 从过往版本
  • Could not transfer artifact xxx from/to xxx解决方案

    在做Openmeetings二次开发的时候install时出现了如下错误 INFO Parent project loaded span class hljs keyword from span repository org apache
  • MavenInvocationException解决方案

    在编译Openmeetings的时候出现了这样的错误信息 xff1a MavenInvocationException Error configuring command line Reason Maven executable not f
  • 生成生命周期介绍

    翻译自http maven apache org guides introduction introduction to the lifecycle html 目录 目录生成生命周期基础 生成生命周期由阶段组成通用命令行调用一个生成阶段是由
  • Crypto++库在VS 2013中的使用 + 基于操作模式AES加密

    一 下载Crypto 43 43 Library Crypto 43 43 Library的官方网 xff1a http www cryptopp com 二 建立自己使用的Crypto 43 43 Library 由于从官方网下载的Cry
  • MATLAB工具箱路径缓存

    关于MATLAB工具箱路径缓存 出于性能考虑 xff0c MATLAB将跨会话缓存工具箱文件夹信息 缓存特性对您来说是透明的 但是 xff0c 如果MATLAB没有找到您的最新版本的MATLAB代码文件 xff0c 或者如果您收到有关工具箱
  • MySQL语法

    初识MySQL 为什么学习数据库 1 岗位技能需求 2 现在的世界 得数据者得天下 3 存储数据的方法 4 程序 网站中 大量数据如何长久保存 5 数据库是几乎软件体系中最核心的一个存在 什么是数据库 数据库 DataBase 简称DB 概

随机推荐

  • HBase Configuration过程

    HBase客户端API中 xff0c 我们看到对HBase的任何操作都需要首先创建HBaseConfiguration类的实例 为HBaseConfiguration类继承自Configuration类 xff0c 而Configurati
  • 中国版的 Github:gitee.com、coding.net

    https gitee com 码云 社区版 主要功能代码托管 xff1a 对个人开发者提供免费的云端 Git 仓库 xff0c 可创建最多 1000 个项目 xff0c 不限公有或私有 xff0c 支持SSH HTTP SVN xff1b
  • winScp 连接 FilEZillA报(由于目标计算机积极拒绝,无法连接)

    场景 xff1a 服务器一台 xff1b 本地台式机一台 xff0c 为了文件传输方便 xff0c 在服务器上使用FilEZillA搭建了FTP xff0c 在本地使用WinScp进行连接 问题 xff1a 首先FTP搭建没问题 xff0c
  • 关于 Raspberry Pi3 使用 Intel® RealSense™ D400 cameras的简单介绍

    Raspberry Pi Raspberry pi 可以称为个人微型电脑 xff0c 虽然它的性能无法与普通电脑相比 xff0c 但是它在很多方面都给我们带来了惊喜 xff0c 它的特点是便于携带 xff0c 功能基本和普通电脑一样 xff
  • 安装好后 实例启动出现问题

    错误如上正在排错中 File 34 usr lib python2 7 site packages nova conductor manager py 34 line 671 in build instances request spec
  • gazebo仿真之plugin系列一

    官网教程 xff1a http gazebosim org tutorials tut 61 plugins hello world amp cat 61 write plugin 本次内容涉及五个方面 xff1a plugin的基本介绍与
  • gazebo官网教程之快速开始

    英文教程 xff1a http gazebosim org tutorials tut 61 quick start amp cat 61 get started 一 运行gazebo 打开有默认环境的gazebo只需要三步 xff1a 1
  • 基于unity无人机3D仿真《一》

    基于unity无人机3D仿真 一 实现无人机的模型的制作 运动学关系 姿态角等 xff1b 实现无人机各种姿态运动 一 目前的效果 二 无人机模型 制作软件 xff1a maya 模型结构 xff1a 三 开发平台 unity2017 43
  • 比特、字节转换

    1bite xff08 比特 xff09 61 1字节 数字 xff1a 1字符 61 1字节 英文 xff1a 1字符 61 1字节 汉字 xff1a 1字符 61 2字节 在ASCII码中 xff0c 一个英文字母 xff08 不分大小
  • Unity无人机仿真github项目

    本人本科生有幸得到导师的指导 xff0c 对Unity这个平台学习已有一段时间 该平台在搭建自主仿真平台方面确实有很大优势 下面是在学习过程中收集到的一些多旋翼无人机仿真的github项目 xff0c 可供需要的快速学习 xff08 推荐先
  • matlab2020a中使用TrueTime工具

    环境 xff1a matlab版本 xff1a 2020a 参考文章 网络控制系统仿真 xff1a Truetime2 0工具箱安装 xff08 win10 43 matlab R2017b xff09 目标 xff1a 在matlab20
  • ros的init机制续篇

    这篇博客主要探讨init的实现过程 ros span class token double colon punctuation span span class token function init span span class toke
  • 基于Unity构建机器人的数字孪生平台系列1—介绍

    1 0 简介 本系列博客将开源近两年结合Unity和多旋翼无人机的相关工作 xff0c 涵盖仿真 建模 全局云端通信网络 本地局部通信网络 ROS 43 Unity VR等方面内容 该工作完整构建以虚控实 xff0c 沉浸式VR交互 xff
  • 基于Unity构建机器人的数字孪生平台系列2—四旋翼无人机三维模型

    系列2的主要内容是探讨如何自己构建一个模型并且导入Unity 1 简介 3D仿真与其他类型仿真的一大区别是三维场景和三维模型 为了实现对某个对象的仿真 xff0c 模型是必须的 当然 xff0c 针对不同的仿真任务 xff0c 需要描述对象
  • 模式识别实现之人脸识别(matlab)

    描述 用有监督学习机制设计并实现模式识别方法 xff0c 用于进行人脸面部特征识别 xff0c 如性别 xff08 男性 女性 xff09 年龄 xff08 儿童 青少年 成年 老年 xff09 佩戴眼镜 xff08 是 否 xff09 戴
  • 无人机自动驾驶GAAS学习一

    building GAAS environment 基本依赖项 pip install pandas jinja2 pyserial cerberus pyulog numpy toml pyquaternion Q1 程序 pip 尚未安
  • 无人机学习之launch文件的学习

    官网教程 xff1a http wiki ros org roslaunch XML xff08 1 xff09 ros系统launch文件出现的原因 xff1a 一个功能的实现包括比较多的节点的运行 xff0c 并且每个节点的启动是有顺序
  • 配置最基础的linux系统——Centos6.x版本

    自然是用到虚拟机了 xff0c Vmware是我常用的 xff0c 这里建立一个虚拟的裸机很简单 xff0c 有两点是要说明的 1 最大磁盘大小 xff0c 这个默认的是最小大小 xff0c 不能设的别它还小了 xff0c 否则启动不了Ce
  • matlab实现画散点图(一个x对应多个y)

    1 具体实现是 xff0c 首选导入数据 aray 61 importdata 位置 xff1b m n 61 size array 2 x轴间距设置 x 61 1 1 m 3 处理数组数据 figure 1 for i 61 1 1 n
  • VINS-Mono源码分析7— pose_graph2(四自由度位姿图优化)

    VINS Mono源码分析7 pose graph2 在上一篇博文中 xff0c 大概分析了一下VINS Mono回环检测和重定位的代码实现 xff0c 这里主要分析 四自由度的位姿图优化 关于这部分的原理可以参考VINS Mono论文第8