VINS-MONO学习笔记 [基于滑动窗口的非线性优化]

2023-05-16

在这里插入图片描述
在这里插入图片描述

目录

    • 1. 代码
    • 2. ceres解析求导
    • 3. ceres李代数加法代码实现
    • 4. 预积分约束残差计算
    • 5. 预积分雅克比计算
    • 6. 视觉重投影约束
    • 7. 滑动窗口边缘化

1. 代码

  • 代码位置:
    vins_estimator->src->estimator.cpp
if(result)//初始化成功
{
   //先进行一次滑动窗口非线性优化,得到当前帧与第一帧的位姿
   solver_flag = NON_LINEAR;
   // step4:非线性优化求解vio
   solveOdometry();
   // step5:滑动窗口
   slideWindow();
   // step6: 移除无效地图点
   f_manager.removeFailures();
   ROS_INFO("Initialization finish!");
   last_R = Rs[WINDOW_SIZE];
   last_P = Ps[WINDOW_SIZE];
   last_R0 = Rs[0];
   last_P0 = Ps[0];
 }
//三角化求解所有特征点的深度,并进行非线性优化
void Estimator::solveOdometry()
{
    // 保证滑窗中帧数满了
    if (frame_count < WINDOW_SIZE)
        return;
    // 其次要求初始化完成
    if (solver_flag == NON_LINEAR)
    {
        TicToc t_tri;
        // 先把应该三角化但是没有三角化的特征点三角化
        f_manager.triangulate(Ps, tic, ric);
        ROS_DEBUG("triangulation costs %f", t_tri.toc());
        // 非线性优化 
        optimization();
    }
}

2. ceres解析求导

解析求导相比于自动求导,会使程序的速度加快。特别是对于里程计模块这种对于实时性要求很高的程序而言。

需要手动求出每次优化的残差和雅可比
在这里插入图片描述

ceres官网

在这里插入图片描述
因为在vins中我们知道参数块的大小,所以我在vins中使用SizeCostFunction

  • 第一步是新建一个类继承SizeCostFunction
    ceres::SizeCostFunction<1,1> : 前一个1:残差的维度; 后一个1:参数块的维度,因为这里只有一个参数块,所以只有一个1
  • 第二步:定义完类之后需要重载Evaluate虚函数
    double const* const* parameters对应各个参数块.由于各个参数块都定义为double数组,如果有好多个参数块就会被定义为数组的数组,也就是这边的双指针.
  • residuals是一个向量,也就是一个一维数组,所以这里定义为double *
  • jacobians的大小是残差*参数块总数的矩阵,对于矩阵来讲它也是一个二维数组,所以定义为double * *
const double x = parameters[0][0] // 表示x是第0个参数块的第0个参数

LOSS Function 核函数
在这里插入图片描述

http://ceres-solver.org/nnls_modeling.html#instances

3. ceres李代数加法代码实现

// 由于位姿(李群)不满足正常的加法,因此需要自己定义他的加法
ceres::LocalParameterization *local_parameterization = new PoseLocalParameterization();
// 重新定义LocalParameterization局部参数块,继承以下四个函数
class PoseLocalParameterization : public ceres::LocalParameterization
{
    virtual bool Plus(const double *x, const double *delta, double *x_plus_delta) const;
    virtual bool ComputeJacobian(const double *x, double *jacobian) const;
    // 3个平移+4个旋转(四元数)
    virtual int GlobalSize() const { return 7; };
    // 实际的自由度
    virtual int LocalSize() const { return 6; };
};
/**
*  x: 参数
*  delta: 增量
*  x_plus_delta:加法之后的结果
*/
bool PoseLocalParameterization::Plus(const double *x, const double *delta, double *x_plus_delta) const
{
    // Eigen::Map 把double类型映射为 Eigen::Vector3d 类型
    Eigen::Map<const Eigen::Vector3d> _p(x);       // 取了x的前三维
    Eigen::Map<const Eigen::Quaterniond> _q(x + 3);// 取了x的第四维开始的四个维度,也就是四元数

    Eigen::Map<const Eigen::Vector3d> dp(delta);

    Eigen::Quaterniond dq = Utility::deltaQ(Eigen::Map<const Eigen::Vector3d>(delta + 3));

    Eigen::Map<Eigen::Vector3d> p(x_plus_delta);
    Eigen::Map<Eigen::Quaterniond> q(x_plus_delta + 3);

    p = _p + dp;
    // .normalized(): 只有单位四元数才能表示旋转
    q = (_q * dq).normalized();

    return true;
}

在这里插入图片描述

template <typename Derived>
    static Eigen::Quaternion<typename Derived::Scalar> deltaQ(const Eigen::MatrixBase<Derived> &theta)
    {
        typedef typename Derived::Scalar Scalar_t;

        Eigen::Quaternion<Scalar_t> dq;
        Eigen::Matrix<Scalar_t, 3, 1> half_theta = theta;
        half_theta /= static_cast<Scalar_t>(2.0);
        dq.w() = static_cast<Scalar_t>(1.0);
        dq.x() = half_theta.x();
        dq.y() = half_theta.y();
        dq.z() = half_theta.z();
        return dq;
    }

4. 预积分约束残差计算

预积分约束:

在这里插入图片描述

  • 如果仅通过IMU积分得到 b k b_k bk b k + 1 b_{k+1} bk+1的位姿,速度和旋转,那么(25)式是成立的.
  • 但实际还会收到最小化重投影误差的约束
  • 所以最小化 [ 两帧间位姿(速度/旋转)计算出的增量 - IMU预积分得到的增量 ],残差如公式(25)下部分 所示。
    在这里插入图片描述
IMUFactor* imu_factor = new IMUFactor(pre_integrations[j]);
problem.AddResidualBlock(imu_factor, NULL, para_Pose[i], para_SpeedBias[i], para_Pose[j], para_SpeedBias[j]);
// 计算给定相邻帧状态量的残差
    Eigen::Matrix<double, 15, 1> evaluate(const Eigen::Vector3d &Pi, const Eigen::Quaterniond &Qi, const Eigen::Vector3d &Vi, const Eigen::Vector3d &Bai, const Eigen::Vector3d &Bgi,
                                          const Eigen::Vector3d &Pj, const Eigen::Quaterniond &Qj, const Eigen::Vector3d &Vj, const Eigen::Vector3d &Baj, const Eigen::Vector3d &Bgj)
    {
        Eigen::Matrix<double, 15, 1> residuals;

        Eigen::Matrix3d dp_dba = jacobian.block<3, 3>(O_P, O_BA);
        Eigen::Matrix3d dp_dbg = jacobian.block<3, 3>(O_P, O_BG);

        Eigen::Matrix3d dq_dbg = jacobian.block<3, 3>(O_R, O_BG);

        Eigen::Matrix3d dv_dba = jacobian.block<3, 3>(O_V, O_BA);
        Eigen::Matrix3d dv_dbg = jacobian.block<3, 3>(O_V, O_BG);

        Eigen::Vector3d dba = Bai - linearized_ba;
        Eigen::Vector3d dbg = Bgi - linearized_bg;

        // delta_q,delta_v,delta_p:预积分量
        // corrected_:补偿后的修正量 
        Eigen::Quaterniond corrected_delta_q = delta_q * Utility::deltaQ(dq_dbg * dbg);
        Eigen::Vector3d corrected_delta_v = delta_v + dv_dba * dba + dv_dbg * dbg;
        Eigen::Vector3d corrected_delta_p = delta_p + dp_dba * dba + dp_dbg * dbg;

        // 公式(25)下半部份
        residuals.block<3, 1>(O_P, 0) = Qi.inverse() * (0.5 * G * sum_dt * sum_dt + Pj - Pi - Vi * sum_dt) - corrected_delta_p;
        residuals.block<3, 1>(O_R, 0) = 2 * (corrected_delta_q.inverse() * (Qi.inverse() * Qj)).vec();
        residuals.block<3, 1>(O_V, 0) = Qi.inverse() * (G * sum_dt + Vj - Vi) - corrected_delta_v;
        residuals.block<3, 1>(O_BA, 0) = Baj - Bai;
        residuals.block<3, 1>(O_BG, 0) = Bgj - Bgi;
        return residuals;
    }

因为ceres不像g2o,没有增加信息矩阵的接口.
所以不能直接使用 e T ∗ p ∗ e e^T*p*e eTpe,而是将p分解为 L ∗ L T L*L^T LLT,让式子等于 e T ∗ L ∗ L T ∗ e e^T*L*L^T*e eTLLTe. 认为 L T ∗ e L^T*e LTe即为新的残差

        // LLT分解,residual 还需乘以信息矩阵的sqrt_info
        // 因为优化函数其实是d=r^T P^-1 r ,P表示协方差,而ceres只接受最小二乘优化
        // 因此需要把P^-1做LLT分解,使d=(L^T r)^T (L^T r) = r'^T r
        Eigen::Matrix<double, 15, 15> sqrt_info = Eigen::LLT<Eigen::Matrix<double, 15, 15>>(pre_integration->covariance.inverse()).matrixL().transpose();
        // 这就是带有信息矩阵的残差,e = L^T*e
        residual = sqrt_info * residual;

5. 预积分雅克比计算

在这里插入图片描述

在这里插入图片描述

  • 对于雅可比矩阵的第一个分块(jacobians[0] 15X7)

在这里插入图片描述

jacobian_pose_i.block<3, 3>(O_P, O_P) = -Qi.inverse().toRotationMatrix();
jacobian_pose_i.block<3, 3>(O_P, O_R) = Utility::skewSymmetric(Qi.inverse() * (0.5 * G * sum_dt * sum_dt + Pj - Pi - Vi * sum_dt));

在这里插入图片描述

jacobian_pose_i.block<3, 3>(O_V, O_R) = Utility::skewSymmetric(Qi.inverse() * (G * sum_dt + Vj - Vi));

在这里插入图片描述

在这里插入图片描述

Eigen::Quaterniond corrected_delta_q = pre_integration->delta_q * Utility::deltaQ(dq_dbg * (Bgi - pre_integration->linearized_bg));
//.bottomRightCorner<3, 3>():取右下3x3的小块
jacobian_pose_i.block<3, 3>(O_R, O_R) = -(Utility::Qleft(Qj.inverse() * Qi) * Utility::Qright(corrected_delta_q)).bottomRightCorner<3, 3>();

整体代码

			// 第i帧的IMU位姿 pbi、qbi
            if (jacobians[0])
            {
                Eigen::Map<Eigen::Matrix<double, 15, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);
                jacobian_pose_i.setZero();

                jacobian_pose_i.block<3, 3>(O_P, O_P) = -Qi.inverse().toRotationMatrix();
                jacobian_pose_i.block<3, 3>(O_P, O_R) = Utility::skewSymmetric(Qi.inverse() * (0.5 * G * sum_dt * sum_dt + Pj - Pi - Vi * sum_dt));

#if 0
            jacobian_pose_i.block<3, 3>(O_R, O_R) = -(Qj.inverse() * Qi).toRotationMatrix();
#else
                Eigen::Quaterniond corrected_delta_q = pre_integration->delta_q * Utility::deltaQ(dq_dbg * (Bgi - pre_integration->linearized_bg));
                jacobian_pose_i.block<3, 3>(O_R, O_R) = -(Utility::Qleft(Qj.inverse() * Qi) * Utility::Qright(corrected_delta_q)).bottomRightCorner<3, 3>();
#endif

                jacobian_pose_i.block<3, 3>(O_V, O_R) = Utility::skewSymmetric(Qi.inverse() * (G * sum_dt + Vj - Vi));

                jacobian_pose_i = sqrt_info * jacobian_pose_i;

                if (jacobian_pose_i.maxCoeff() > 1e8 || jacobian_pose_i.minCoeff() < -1e8)
                {
                    ROS_WARN("numerical unstable in preintegration");
                    //std::cout << sqrt_info << std::endl;
                    //ROS_BREAK();
                }
            }
  • 对于雅可比矩阵的第三个分块(jacobians[2] 15X7)

在这里插入图片描述

  • 对于雅可比矩阵的第二个分块(jacobians[1] 15X9)

36:07

jacobian_speedbias_i.block<3, 3>(O_P, O_V - O_V) = -Qi.inverse().toRotationMatrix() * sum_dt;
jacobian_speedbias_i.block<3, 3>(O_P, O_BA - O_V) = -dp_dba;
jacobian_speedbias_i.block<3, 3>(O_P, O_BG - O_V) = -dp_dbg;

在这里插入图片描述

jacobian_speedbias_i.block<3, 3>(O_V, O_V - O_V) = -Qi.inverse().toRotationMatrix();
jacobian_speedbias_i.block<3, 3>(O_V, O_BA - O_V) = -dv_dba;
jacobian_speedbias_i.block<3, 3>(O_V, O_BG - O_V) = -dv_dbg;

在这里插入图片描述

在这里插入图片描述

jacobian_speedbias_i.block<3, 3>(O_BA, O_BA - O_V) = -Eigen::Matrix3d::Identity();
jacobian_speedbias_i.block<3, 3>(O_BG, O_BG - O_V) = -Eigen::Matrix3d::Identity();

#pic_center

jacobian_speedbias_i.block<3, 3>(O_R, O_BG - O_V) = -Utility::Qleft(Qj.inverse() * Qi * pre_integration->delta_q).bottomRightCorner<3, 3>() * dq_dbg;
  • 对于雅可比矩阵的第四个分块(jacobians[3] 15X9)

在这里插入图片描述

6. 视觉重投影约束

在这里插入图片描述

  • 第i帧是滑窗中第一个观测到这个3D点的帧
  • 3D点的状态量维持的是逆深度,而不是传统的三维向量
  • 因为在 J T ∗ J ∗ △ x = − J ∗ f ( x ) J^T*J*△x = -J*f(x) JTJx=Jf(x)中, J T ∗ J J^T*J JTJ的维度与 △ x △x x维度相同,而 x x x在VIO问题中维护的是滑窗中的位姿,外参以及(占比重最大的)3D点,对3D点只存储逆深度(1个维度),可以让x的维度相比维护3D点坐标(3个维度)缩小2/3,加快求解速度
  • 为什么用逆深度不用深度:对于极远的点(例如天空),正深度为无穷大,逆深度则为一个很小的值.而且不会存在离相机很近的点,不存在逆深度值很大的情况
  • 因为不是存储三维坐标,所以逆深度需要和第i帧绑定
  • 优化变量:第i帧IMU的位姿,第j帧IMU的位姿,相机到IMU外参,3D点逆深度

在这里插入图片描述
λ \lambda λ表示逆深度, 1 / λ 1/\lambda 1/λ表示深度
推导一下:

在这里插入图片描述

ProjectionFactor *f = new ProjectionFactor(pts_i, pts_j); // 构造函数就是同一个特征点在不同帧的观测
// 约束的变量是该特征点的第一观测帧以及其他一个观测帧,加上外参和特征点逆深度
problem.AddResidualBlock(f, loss_function, para_Pose[imu_i], para_Pose[imu_j], para_Ex_Pose[0], para_Feature[feature_index]);

残差的计算:
在这里插入图片描述如果把上面的一长串带入到残差,需要把一长串分为x,y,z三个部分。这会非常的复杂,所以我们采用链式求导法则。
以x为例,我们把残差关于待优化变量的导数分解为残差对于第j帧相机坐标系下的点的导数 × 第j帧相机坐标系下点对于各个优化变量的导数
在这里插入图片描述在这里插入图片描述在这里插入图片描述

		if (jacobians[0]) 
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);

            Eigen::Matrix<double, 3, 6> jaco_i;
            jaco_i.leftCols<3>() = ric.transpose() * Rj.transpose();
            jaco_i.rightCols<3>() = ric.transpose() * Rj.transpose() * Ri * -Utility::skewSymmetric(pts_imu_i);

            jacobian_pose_i.leftCols<6>() = reduce * jaco_i;
            jacobian_pose_i.rightCols<1>().setZero();
        }

在这里插入图片描述

		// 对第j帧的位姿 pbj,qbj
        if (jacobians[1])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_j(jacobians[1]);

            Eigen::Matrix<double, 3, 6> jaco_j;
            jaco_j.leftCols<3>() = ric.transpose() * -Rj.transpose();
            jaco_j.rightCols<3>() = ric.transpose() * Utility::skewSymmetric(pts_imu_j);

            jacobian_pose_j.leftCols<6>() = reduce * jaco_j;
            jacobian_pose_j.rightCols<1>().setZero();
        }

在这里插入图片描述

		// 对相机到IMU的外参 pbc,qbc (qic,tic)
        if (jacobians[2])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_ex_pose(jacobians[2]);
            Eigen::Matrix<double, 3, 6> jaco_ex;
            jaco_ex.leftCols<3>() = ric.transpose() * (Rj.transpose() * Ri - Eigen::Matrix3d::Identity());
            Eigen::Matrix3d tmp_r = ric.transpose() * Rj.transpose() * Ri * ric;
            jaco_ex.rightCols<3>() = -tmp_r * Utility::skewSymmetric(pts_camera_i) + Utility::skewSymmetric(tmp_r * pts_camera_i) +
                                     Utility::skewSymmetric(ric.transpose() * (Rj.transpose() * (Ri * tic + Pi - Pj) - tic));
            jacobian_ex_pose.leftCols<6>() = reduce * jaco_ex;
            jacobian_ex_pose.rightCols<1>().setZero();
        }

在这里插入图片描述

	    if (jacobians[3])
        {
            Eigen::Map<Eigen::Vector2d> jacobian_feature(jacobians[3]);
            jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i * -1.0 / (inv_dep_i * inv_dep_i);
        }

7. 滑动窗口边缘化

在这里插入图片描述

由于需要边缘化,所以我们要手动计算H矩阵,而不能靠ceres帮我们自动计算
在这里插入图片描述
在这里插入图片描述https://blog.csdn.net/heyijia0327/article/details/52822104

  • p是位姿,m是地图点,连线表示约束

  • X p 1 X_{p1} Xp1 X m 1 X_{m1} Xm1的贡献是{p1,p1},{p1,m1},{m1,p1},{m1,m1}; X p 1 X_{p1} Xp1 X p 2 X_{p2} Xp2的贡献是{p1,p1},{p1,p2},{p2,p1},{p2,p2}

在这里插入图片描述

  • Λ a Λa Λa ={p1,p1}, Λ b Λb Λb = {p1,p2-m6}, Λ c Λc Λc={p2-m6,p2-m6}

上式=在这里插入图片描述=
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VINS-MONO学习笔记 [基于滑动窗口的非线性优化] 的相关文章

  • 利用natapp做内网穿透

    有时候我们自己做个小网站 xff0c 或者开发微信公众号做测试时 xff0c 因为没有公网ip而犯难 xff0c 毕竟买一台云服务器带独立ip的 xff0c 加上域名是一笔不小的开支 这时如果有一个内网穿透工具 xff0c 让自己本地电脑充
  • 权限管理系统【SpringBoot + Vue + SpringSecurity】

    文章目录 项目介绍技术选型环境要求项目结构表结构项目地址展示效果图 项目介绍 基于Springboot 构建的 前后端分离 通用权限管理系统 技术选型 后端技术 Spring Boot xff0c MyBatis plus xff0c Sp
  • JVM虚拟机【面试看这一篇就足够了】

    文章目录 前言内存结构说说JVM内存整体的结构 xff1f 线程私有还是共享的 xff1f 什么是程序计数器 xff08 线程私有 xff09 xff1f 什么是虚拟机栈 xff08 线程私有 xff09 xff1f 什么是本地方法栈 xf
  • 在Ubuntu18上用LibTorch(非darknet)部署YOLOv4的填坑之路(附源码)

    目录 坑一 xff1a 从官网下载的LibTorch库是不带torchvision的 坑二 xff1a Python的PIL库与opencv库在图像处理上的差异值得注意 坑三 xff1a LibTorch对tensor的各种变换操作度相比P
  • 锁升级过程(无锁、偏向锁、轻量级锁、重量级锁)

    文章目录 Synchronized锁升级的背景Synchronized的性能变化Java5之前 xff0c 用户态和内核态之间的切换为什么每个对象都可以称为一把锁 xff1f Java6开始优化Synchronized Synchroniz
  • String.format()的详细用法

    问题 在开发中一段字符串的中间某一部分需要可变的 如 xff1a 文本要展示 张三用户来自深圳 xff0c 年龄 18 xff0c 性别 男 其中的 张三 是用户名 xff0c 每个用户都是不一样的 xff1b 地区 深圳 为可变的Stri
  • Element实现行合并

    效果图 完整代码 span class token tag span class token tag span class token punctuation lt span template span span class token p
  • @EnableAsync @Async

    一直不太忙白线程池在实际应用中到底扮演什么样得角色 xff0c 有什么场景可以使用到 xff0c 只有真正做项目中使用到了才逐渐理解 使用多线程 xff0c 往往是创建Thread xff0c 或者是实现Runnable接口 xff0c 用
  • Java实现QQ邮箱发送给网易邮箱,发送邮件+附件

    实现功能 xff1a Java实现 QQ邮箱 给网易邮箱发送邮件获取邮件信息并下载邮件的附件 前置功能 案例以QQ邮箱为例说明 xff1a 首先登录QQ邮箱将SMTP服务开启 xff0c 会得到一个授权码 创建一个Springboot项目
  • kubernetes(v1.21.10)简介

    学习视频地址 xff1a https www bilibili com video BV13Q4y1C7hS 1 背景 1 部署方式的演变 传统部署时代 xff1a 在物理机服务器上运行应用程序 无法为应用程序定义资源边界 导致资源分配问题
  • Kubernetes(v1.21.10)集群安装

    视频中安装的是v1 20 9 xff0c 我们安装kubernetes的v1 21 10版本 1 环境规划 1 集群类型 Kubernetes 集群大致分为两类 xff1a 一主多从和多主多从 一主多从 xff08 单 master xff
  • Kubernetes(v1.21.10)实战入门与组件说明

    文章目录 一 资源管理1 资源管理介绍2 YAML语言介绍3 资源管理方式3 1 命令式对象管理kubectl命令资源类型 type 操作 command 3 2 命令式对象配置3 3 声明式对象配置3 4 总结 二 kubernetes组
  • 嵌入式系统(一):鸿蒙系统

    本文为期末复习笔记 xff0c 内容仅供参考 x1f600 鸿蒙系统是一款面向万物互联时代的 全新的分布式操作系统 鸿蒙提出了基于同一套系统能力 适配多种终端形态的分布式理念提供全场景 xff08 移动办公 运动健康 社交通信 指挥出行 媒
  • 【机组】--总线-例题

    例题1 某总线时钟频率为 66 M H z 66MHz 6 6 M H z xff0c 在一个
  • ROS:运行usb摄像头报错 No JPEG data found in image

    将usb cam功能包从GitHub上clone到ROS的工作空间 xff0c 编译后运行如下命令 xff1a rosrun usb cam usb cam node 然后报错如下 mjpeg 64 0x55e5f4e34120 No JP
  • breakpad简单使用

    breakpad简单使用 文章目录 1 breakpad简介2 源码下载3 源码编译3 1 编译遇到的问题 4 In Process测试程序 xff1a 4 1 测试程序编译4 2 生成sym文件4 3 产生minidump文件4 4 分析
  • 树莓派+PX4固件+T265+MAVROS+QGC实现室内定位

    树莓派 43 PX4固件 43 T265 43 MAVROS 43 QGC实现室内定位 整了整整两礼拜 终于稍微搞明白了一点PX4和MAVROS 因为APM固件下MISSIONPLANNER地面站只能windows下 觉得windows和U
  • Python调用sumo,解决自定义vehicle颜色等问题

    Python调用SUMO接口 xff0c 解决vehicle颜色设置问题 1 Python调用sumo 的traci接口 xff0c 通过 traci vehicle setColor进行设置 以下是 示例代码 span class tok
  • 反向散射通信

    原文 xff1a Ambient Backscatter Communications A Contemporary Survey 反向散射通信 反向散射通信系统根据其体系结构可分为三大类 xff1a a 单基地反向散射通信系统 xff08
  • ESP8266+Micropython+OLED网络天气和时钟

    在网上查了很多的资料 xff0c 打算做一个可以按键显示网络时间和天气预报的东西 手头有WeMos nodeMCU和I2C接口的OLED12864 xff0c 不打算用时钟模块 xff0c 因为ESP8266可以上网 xff0c 完全可以借

随机推荐

  • ros入门与控制无人机

    一 概述 总体来说 xff0c 分为三步 step1 xff1a roscore step2 xff1a 发布话题 step3 xff1a 订阅话题并显示 二 roscore roscore 是你在运行所有ROS程序前首先要运行的命令 现理
  • Oracle创建新用户

    Oracle 创建新用户 SQL语句 span class token operator span span class token operator span span class token operator span 创建用户 cre
  • Centos8.2安装配置 VNC 远程桌面Xfce、解决输入法VNC无法使用的问题、解决浏览器无法播放视频的问题

    本文使用MobaXterm Xshell Xfce TightVNC来搭建百度智能云Centos8 2 同时解决解决输入法VNC无法使用的问题 解决浏览器无法播放视频的问题 目录 一 Windows软件列表 MobaXterm TightV
  • python自定义函数 def 的奇妙世界

    自定义函数 def注意事项案例分析作死挑战函数参数形参与实参位置参数关键字参数默认参数必选参数可变参数可变关键字参数 参数问题解答 这里是三岁 xff0c 来和大家唠唠自定义函数 xff0c 这一个神奇的东西 xff0c 带大家白话玩转自定
  • Qt 怎么设置黑色背景/黑色主题?

    目录 整体分为三步1 更改主题颜色1 1 更改颜色 xff1a 1 2 重启 Qt1 3 第一阶段完成 xff0c 效果如下所示 2 更改编辑器颜色2 1 更改颜色2 2 第二阶段完成 xff0c 效果如下所示 3 更改 Qt 窗口上边框颜
  • YOLOv4在ROS-Melodic上的部署-libtorch(附源码,非Darknet)

    有关YOLOv4 LibTorch的部署可以看我的这篇博客 然后移植到ROS Melodic上就相对简单了 直接上GitHub链接吧 初版可能有点乱 xff0c 但是注释挺详细 xff0c 欢迎交流经验哈 2020 12 16 更新 YOL
  • 手把手安装Windows11虚拟机

    创建虚拟机 想要在虚拟机当中安装windows11系统 xff0c 首先我们就要在Vmware中创建一个虚拟机 这个操作比较简单 xff0c 只需要打开新建虚拟机向导 xff0c 按照提示操作就可以了 在创建虚拟机的时候 xff0c 安装w
  • C++ 学习过程中遇到的难题及解决 (笔记,随更

    C 43 43 报错表达式必须含有常量值 原因 xff1a c 43 43 中不允许使用变量作为数组的长度定义数组 xff0c 必须为常量值 xff0c c 43 43 中所有的内存需求都是在程序执行前通过定义的常量来确定的 C 43 43
  • 一篇文章快速提高Python能力

    确实当你学了Python的一段时间之后会有一阵不知道如何更快的提高自己的功力 其实Python也是语言的一种 xff0c 虽然语法很简单 xff0c 但是内涵真的很丰富 xff0c 招式非常的多 修炼武功我个人觉得分几个层次 第一招是语法
  • python 图形界面库对比合集

    从 Python 语言的诞生之日起 xff0c 就有许多优秀的 GUI 工具集整合到 Python 当中 xff0c 这些优秀的 GUI工具集 xff0c 使得 Python 也可以在图形界面编程领域当中大展身手 xff0c 由于 Pyth
  • TensorFlow深度学习及教程分享

    TensorFlow深度学习框架 Google不仅是大数据和云计算的领导者 xff0c 在机器学习和深度学习上也有很好的实践和积累 xff0c 在2015年年底开源了内部使用的深度学习框架TensorFlow 与Caffe Theano T
  • Python 中国象棋源码 V1

    Pygame 做的中国象棋 xff0c 一直以来喜欢下象棋 xff0c 写了 python 就拿来做一个试试 xff0c 水平有限 xff0c 电脑走法水平低 xff0c 需要在下次版本中更新电脑走法 xff0c 希望源码能帮助大家更好的学
  • python数据爬下来保存在哪里

    昨天下班后忽然兴起想写一个爬虫抓抓网页上的东西 花了一个钟简单学习了python的基础语法 xff0c 然后参照网上的例子自己写了个爬虫 xff08 推荐学习 xff1a Python视频教程 xff09 python数据爬下来保存在本地
  • python求平均值

    python求平均值 首先我们先来了解一下计算平均数的IPO模式 输入 xff1a 待输入计算平均数的数 处理 xff1a 平均数算法 输出 xff1a 平均数 推荐 xff1a python教程 明白了程序的IPO模式之后 xff0c 我
  • 如何用for循环实现一个无限循环

    使用while True可以轻松的实现一个无限循环 xff0c 也叫死循环 xff0c 那么for循环能够实现类似的功能么 xff1f 当然可以 xff0c 借助itertools模块的cycle函数就可以 import time from
  • 闲得发慌之趣味技能:python之猫脸检测

    Python 小猫检测 xff0c 通过调用opencv自带的猫脸检测的分类器进行检测 分类器有两个 xff1a haarcascade frontalcatface xml和 haarcascade frontalcatface exte
  • 目标检测性能的衡量指标:mAP图解

    最近又重新复习了下mAP xff0c 在网上找了有关mAP的文章 xff0c 但是感觉大多数都是左抄一点又抄一点那种 写的比较乱 xff0c 可能因为mAP的计算方法前后变过几次 xff0c 所以导致有很多版本的说法存在 为了更好的理解 x
  • 热门编程——python

    python是干什么的 xff1f 要找出python火爆的原因 xff0c 我们就不得不先来了解python本身 Python 是一种面向对象的解释型计算机程序设计语言 xff0c 由荷兰人 Guido van Rossum 于 1989
  • Python求两个数的最大公约数

    一 求最大公约数算法 整数A对整数B进行取整 余数用整数C来表示 举例 C 61 A B 如果C等于0 则C就是整数A和整数B的最大公约数 如果C不等于0 将B赋值给A 将C赋值给B 然后进行 1 2 两步 直到余数为0 则可以得知最大公约
  • VINS-MONO学习笔记 [基于滑动窗口的非线性优化]

    目录 1 代码2 ceres解析求导3 ceres李代数加法代码实现4 预积分约束残差计算5 预积分雅克比计算6 视觉重投影约束7 滑动窗口边缘化 1 代码 代码位置 xff1a vins estimator gt src gt estim