2021@SDUSC
2021年12月24日星期五——2021年12月27日星期一

一、背景简介:

这一周继续分析factor文件夹。

这一周我分析的代码部分视觉的残差,特征点在相机的投影下产生的残差:projection_factor.cppprojection_td_factor.cpppose_local_parameterization.cpp以及对应的头文件。

首先是原理分析:

回到我们之前分析过的边缘化的总框架图上去,可以看出,这部分的代码是同ceres息息相关的的,是利用ceres Solver来处理问题的。


经过阅读分析,我发现这部分的核心代码就是projection_factor
projetcion_td_factor是它的扩展,pose_local_parameterization是ceres里的一个小函数。

二、代码分析:

1projection_factor.h

对于projection_factor而言,它继承了Ceres库中的SizedCostFunction。

它表示的是第i帧中单位相机平面上的点pts_i重投影到第j帧单位相机平面上的点与匹配点pts_j的投影误差。需要实现构造函数以及重载Evaluate函数。

#pragma once#include <ros/assert.h>
#include <ceres/ceres.h>
#include <Eigen/Dense>
#include "../utility/utility.h"
#include "../utility/tic_toc.h"
#include "../parameters.h"class ProjectionFactor : public ceres::SizedCostFunction<2, 7, 7, 7, 1>
{public:ProjectionFactor(const Eigen::Vector3d &_pts_i, const Eigen::Vector3d &_pts_j);virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const;void check(double **parameters);Eigen::Vector3d pts_i, pts_j;Eigen::Matrix<double, 2, 3> tangent_base;static Eigen::Matrix2d sqrt_info;static double sum_t;
};

2 projection_factor.cpp

构造函数传入的是常数,是一对匹配的特征点,这里传入的数据是匹配点在相机平面上的坐标。
但是重投影误差计算的是单位球面切平面上的误差。(为什么是球平面?)
因此需要利用施密特正交化求出切平面的正交基。

#include "projection_factor.h"Eigen::Matrix2d ProjectionFactor::sqrt_info;
double ProjectionFactor::sum_t;ProjectionFactor::ProjectionFactor(const Eigen::Vector3d &_pts_i, const Eigen::Vector3d &_pts_j) : pts_i(_pts_i), pts_j(_pts_j)
{#ifdef UNIT_SPHERE_ERROREigen::Vector3d b1, b2;Eigen::Vector3d a = pts_j.normalized();Eigen::Vector3d tmp(0, 0, 1);if(a == tmp)tmp << 1, 0, 0;b1 = (tmp - a * (a.transpose() * tmp)).normalized();b2 = a.cross(b1);tangent_base.block<1, 3>(0, 0) = b1.transpose();tangent_base.block<1, 3>(1, 0) = b2.transpose();
#endif
};

bool ProjectionFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{TicToc tic_toc;Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]);Eigen::Quaterniond Qi(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);Eigen::Vector3d Pj(parameters[1][0], parameters[1][1], parameters[1][2]);Eigen::Quaterniond Qj(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);Eigen::Vector3d tic(parameters[2][0], parameters[2][1], parameters[2][2]);Eigen::Quaterniond qic(parameters[2][6], parameters[2][3], parameters[2][4], parameters[2][5]);//pts_i 是i时刻归一化相机坐标系下的3D坐标//第i帧相机坐标系下的的逆深度double inv_dep_i = parameters[3][0];//第i帧相机坐标系下的3D坐标Eigen::Vector3d pts_camera_i = pts_i / inv_dep_i;//第i帧IMU坐标系下的3D坐标Eigen::Vector3d pts_imu_i = qic * pts_camera_i + tic;//世界坐标系下的3D坐标Eigen::Vector3d pts_w = Qi * pts_imu_i + Pi;//第j帧imu坐标系下的3D坐标Eigen::Vector3d pts_imu_j = Qj.inverse() * (pts_w - Pj);//第j帧相机坐标系下的3D坐标Eigen::Vector3d pts_camera_j = qic.inverse() * (pts_imu_j - tic);Eigen::Map<Eigen::Vector2d> residual(residuals);#ifdef UNIT_SPHERE_ERROR residual =  tangent_base * (pts_camera_j.normalized() - pts_j.normalized());
#elsedouble dep_j = pts_camera_j.z();residual = (pts_camera_j / dep_j).head<2>() - pts_j.head<2>();
#endifresidual = sqrt_info * residual;//reduce 表示残差residual对fci(pts_camera_j)的导数,同样根据不同的相机模型if (jacobians){Eigen::Matrix3d Ri = Qi.toRotationMatrix();Eigen::Matrix3d Rj = Qj.toRotationMatrix();Eigen::Matrix3d ric = qic.toRotationMatrix();Eigen::Matrix<double, 2, 3> reduce(2, 3);//下方是对于雅克比矩阵的推导计算
#ifdef UNIT_SPHERE_ERRORdouble norm = pts_camera_j.norm();Eigen::Matrix3d norm_jaco;double x1, x2, x3;x1 = pts_camera_j(0);x2 = pts_camera_j(1);x3 = pts_camera_j(2);norm_jaco << 1.0 / norm - x1 * x1 / pow(norm, 3), - x1 * x2 / pow(norm, 3),            - x1 * x3 / pow(norm, 3),- x1 * x2 / pow(norm, 3),            1.0 / norm - x2 * x2 / pow(norm, 3), - x2 * x3 / pow(norm, 3),- x1 * x3 / pow(norm, 3),            - x2 * x3 / pow(norm, 3),            1.0 / norm - x3 * x3 / pow(norm, 3);reduce = tangent_base * norm_jaco;
#elsereduce << 1. / dep_j, 0, -pts_camera_j(0) / (dep_j * dep_j),0, 1. / dep_j, -pts_camera_j(1) / (dep_j * dep_j);
#endifreduce = sqrt_info * reduce;// 残差项的Jacobian// 先求fci对各项的Jacobian,然后用链式法则乘起来// 对第i帧的位姿 pbi,qbi      2X7的矩阵 最后一项是0if (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,qbjif (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();}// 对逆深度 \lambda (inv_dep_i)if (jacobians[3]){Eigen::Map<Eigen::Vector2d> jacobian_feature(jacobians[3]);
#if 1jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i * -1.0 / (inv_dep_i * inv_dep_i);
#elsejacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i;
#endif}}sum_t += tic_toc.toc();return true;
}

这部分后半段涉及到扰动模型的知识,又是一个难以理解的概念。
简单说来,这里的概念就是对于雅克比矩阵的四个不同的项的求解。

3 pose_local_parameterization.h

这个头文件声明了拓展了ceres solver中关于参数的处理。
主要是声明了四个函数,这四个函数的功能分别是:

  • Plus:平移和旋转的加法实现,主要是旋转,需要处理四元数的运算,注意这个假发的意思是广义加法,目的是在于实现优化变量的更新
  • ComputeJacobian:计算雅克比矩阵
  • GlobalSize:表示了参数的自由度
  • LocalSize:表示了Δx所在的正切空间的自由度
    这是ceres库中相关的一个参数化函数。
#pragma once#include <eigen3/Eigen/Dense>
#include <ceres/ceres.h>
#include "../utility/utility.h"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;virtual int GlobalSize() const { return 7; };virtual int LocalSize() const { return 6; };
};

4pose_local_parameterization.cpp

这个地方是上边介绍的头文件的具体实现。

#include "pose_local_parameterization.h"bool PoseLocalParameterization::Plus(const double *x, const double *delta, double *x_plus_delta) const
{Eigen::Map<const Eigen::Vector3d> _p(x);Eigen::Map<const Eigen::Quaterniond> _q(x + 3);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;q = (_q * dq).normalized();return true;
}
bool PoseLocalParameterization::ComputeJacobian(const double *x, double *jacobian) const
{Eigen::Map<Eigen::Matrix<double, 7, 6, Eigen::RowMajor>> j(jacobian);j.topRows<6>().setIdentity();j.bottomRows<1>().setZero();return true;
}

5 projection_td_factor.h

可以看出来,这个td和没有td的projection_factor极为相似。
不同的是多出的几个变量。
因为头文件中只是声明,具体的不同我们还需要在cpp代码中找出来差异。

    Eigen::Vector3d velocity_i, velocity_j;//角点在归一化平面的速度double td_i, td_j;//处理IMU数据时用到的时间同步误差double row_i, row_j;//角点图像坐标的纵坐标
#pragma once#include <ros/assert.h>
#include <ceres/ceres.h>
#include <Eigen/Dense>
#include "../utility/utility.h"
#include "../utility/tic_toc.h"
#include "../parameters.h"//对imu-camera 时间戳不完全同步和 Rolling shutter 相机的支持
class ProjectionTdFactor : public ceres::SizedCostFunction<2, 7, 7, 7, 1, 1>
{public:ProjectionTdFactor(const Eigen::Vector3d &_pts_i, const Eigen::Vector3d &_pts_j,const Eigen::Vector2d &_velocity_i, const Eigen::Vector2d &_velocity_j,const double _td_i, const double _td_j, const double _row_i, const double _row_j);virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const;void check(double **parameters);Eigen::Vector3d pts_i, pts_j;//角点在归一化平面的坐标Eigen::Vector3d velocity_i, velocity_j;//角点在归一化平面的速度double td_i, td_j;//处理IMU数据时用到的时间同步误差Eigen::Matrix<double, 2, 3> tangent_base;double row_i, row_j;//角点图像坐标的纵坐标static Eigen::Matrix2d sqrt_info;static double sum_t;
};

6 projection_td_factor.cpp

得出的结论依旧是初始化的信息不同……剩下的结构完全一样。

#include "projection_td_factor.h"Eigen::Matrix2d ProjectionTdFactor::sqrt_info;
double ProjectionTdFactor::sum_t;ProjectionTdFactor::ProjectionTdFactor(const Eigen::Vector3d &_pts_i, const Eigen::Vector3d &_pts_j, const Eigen::Vector2d &_velocity_i, const Eigen::Vector2d &_velocity_j,const double _td_i, const double _td_j, const double _row_i, const double _row_j) : pts_i(_pts_i), pts_j(_pts_j), td_i(_td_i), td_j(_td_j)
{velocity_i.x() = _velocity_i.x();velocity_i.y() = _velocity_i.y();velocity_i.z() = 0;velocity_j.x() = _velocity_j.x();velocity_j.y() = _velocity_j.y();velocity_j.z() = 0;row_i = _row_i - ROW / 2;row_j = _row_j - ROW / 2;#ifdef UNIT_SPHERE_ERROREigen::Vector3d b1, b2;Eigen::Vector3d a = pts_j.normalized();Eigen::Vector3d tmp(0, 0, 1);if(a == tmp)tmp << 1, 0, 0;b1 = (tmp - a * (a.transpose() * tmp)).normalized();b2 = a.cross(b1);tangent_base.block<1, 3>(0, 0) = b1.transpose();tangent_base.block<1, 3>(1, 0) = b2.transpose();
#endif
};bool ProjectionTdFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{TicToc tic_toc;Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]);Eigen::Quaterniond Qi(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);Eigen::Vector3d Pj(parameters[1][0], parameters[1][1], parameters[1][2]);Eigen::Quaterniond Qj(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);Eigen::Vector3d tic(parameters[2][0], parameters[2][1], parameters[2][2]);Eigen::Quaterniond qic(parameters[2][6], parameters[2][3], parameters[2][4], parameters[2][5]);double inv_dep_i = parameters[3][0];double td = parameters[4][0];Eigen::Vector3d pts_i_td, pts_j_td;pts_i_td = pts_i - (td - td_i + TR / ROW * row_i) * velocity_i;pts_j_td = pts_j - (td - td_j + TR / ROW * row_j) * velocity_j;Eigen::Vector3d pts_camera_i = pts_i_td / inv_dep_i;Eigen::Vector3d pts_imu_i = qic * pts_camera_i + tic;Eigen::Vector3d pts_w = Qi * pts_imu_i + Pi;Eigen::Vector3d pts_imu_j = Qj.inverse() * (pts_w - Pj);Eigen::Vector3d pts_camera_j = qic.inverse() * (pts_imu_j - tic);Eigen::Map<Eigen::Vector2d> residual(residuals);#ifdef UNIT_SPHERE_ERROR residual =  tangent_base * (pts_camera_j.normalized() - pts_j_td.normalized());
#elsedouble dep_j = pts_camera_j.z();residual = (pts_camera_j / dep_j).head<2>() - pts_j_td.head<2>();
#endifresidual = sqrt_info * residual;if (jacobians){Eigen::Matrix3d Ri = Qi.toRotationMatrix();Eigen::Matrix3d Rj = Qj.toRotationMatrix();Eigen::Matrix3d ric = qic.toRotationMatrix();Eigen::Matrix<double, 2, 3> reduce(2, 3);
#ifdef UNIT_SPHERE_ERRORdouble norm = pts_camera_j.norm();Eigen::Matrix3d norm_jaco;double x1, x2, x3;x1 = pts_camera_j(0);x2 = pts_camera_j(1);x3 = pts_camera_j(2);norm_jaco << 1.0 / norm - x1 * x1 / pow(norm, 3), - x1 * x2 / pow(norm, 3),            - x1 * x3 / pow(norm, 3),- x1 * x2 / pow(norm, 3),            1.0 / norm - x2 * x2 / pow(norm, 3), - x2 * x3 / pow(norm, 3),- x1 * x3 / pow(norm, 3),            - x2 * x3 / pow(norm, 3),            1.0 / norm - x3 * x3 / pow(norm, 3);reduce = tangent_base * norm_jaco;
#elsereduce << 1. / dep_j, 0, -pts_camera_j(0) / (dep_j * dep_j),0, 1. / dep_j, -pts_camera_j(1) / (dep_j * dep_j);
#endifreduce = sqrt_info * reduce;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();}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();}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_td * -1.0 / (inv_dep_i * inv_dep_i);}if (jacobians[4]){Eigen::Map<Eigen::Vector2d> jacobian_td(jacobians[4]);jacobian_td = reduce * ric.transpose() * Rj.transpose() * Ri * ric * velocity_i / inv_dep_i * -1.0  +sqrt_info * velocity_j.head(2);}}sum_t += tic_toc.toc();return true;
}

三、问题及解答:

1 这里面的projection_td_factor和projection_factor什么关系

但看这部分代码并不能发现两者的异同,只是发现了td中多出来的参数。
还需要到调用这两个类的地方去探究异同。

   for (auto &it_per_frame : it_per_id.feature_per_frame){//遍历当前特征在每一帧的信息 imu_j++;if (imu_i == imu_j)         continue;Vector3d pts_j = it_per_frame.point;//!得到第二个特征点if (ESTIMATE_TD)//在有同步误差的情况下{ProjectionTdFactor *f_td = new ProjectionTdFactor(pts_i, pts_j, it_per_id.feature_per_frame[0].velocity, it_per_frame.velocity,it_per_id.feature_per_frame[0].cur_td, it_per_frame.cur_td, t_per_id.feature_per_frame[0].uv.y(), it_per_frame.uv.y());problem.AddResidualBlock(f_td, loss_function, para_Pose[imu_i], para_Pose[imu_j], para_Ex_Pose[0], para_Feature[feature_index], para_Td[0]);}else//在没有同步误差的情况下{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]);}f_m_cnt++;}

这个部分的代码已经很明显了,就是用于处理不同情况下的两帧:有同步误差的情况、没有同步误差的情况。
对应了不同的problem添加残差的函数。

2 施密特正交化

给定的基向量可以通过这个算法得到一组正交化的基。

3 扰动模型

这部分涉及到李群和李代数的相关知识;暂时不去深究。
主要是为了处理矩阵的导数问题……

但是这部分的分析也是代码的重点部分。有时间的话记得要补上。

四、参考资料:

projection_factor代码解析:VINS-MONO
施密特正交化
李代数求导与扰动模型
VINS-MONO ----后端优化
LocalParameterization参数化解析

ROS-3DSLAM(16):视觉部分visual estimator第九节 factor4相关推荐

  1. ROS-3DSLAM(15):视觉部分visual estimator 第九节 factor3

    2021@SDUSC 2021年12月19日星期日--2021年12月24日星期五 一.背景简介: 这一周我分析的代码是factor文件夹 运动模型的IMU计算中得到的残差:相关代码为imu_fact ...

  2. 【java】兴唐课程第五节到第九节知识点总结

    第九节 1. 代码:void readBook(String- bookNames) 表示不确定参数的个数,此时变量为一个数组. 2.当方法中的参数名称(如stuname)和属性名称相同时. this ...

  3. Python编程基础:第九节 逻辑运算Logical Operators

    第九节 逻辑运算Logical Operators 前言 实践 前言 常用的逻辑运算共分为三种:与(and).或(or).非(not).与运算就是同真才真,有假则假:或运算就是有真则真,同假才假:非运 ...

  4. 【Python基础知识-pycharm版】第九节_面向对象的三大特征

    第九节 方法 方法没有重载 私有属性和私有方法(实现封装) @property装饰器_get和set方法 面向对象的三大特征说明(封装.继承.多态) 继承 方法的重写(类成员的继承和重写) 查看类的继 ...

  5. 第九节--绑定 -- Classes and Objects in PHP5 [9](转)

    /* +-------------------------------------------------------------------------------+ | = 本文为Haohappy ...

  6. Kotlin学习笔记 第二章 类与对象 第九节 泛型

    参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...

  7. 《如何搭建小微企业风控模型》第九节 单变量分析(上)节选

    <如何搭建小微企业风控模型>第九节 单变量分析(上)节选 第一章 小微企业数据风控技术的框架 小微企业数据贷发展情况概述 搭建小微企业风控模型所需知识 风控模型概览 第二章 强相关变量:企 ...

  8. 第四章第九节数据资产盘点-数据资产目录分类

    第四章第九节数据资产盘点-数据资产目录分类 在形成数据资产清单以后,如何将清单进行分类?关于数据资产目录的分类,有几种方法,一是参考行业数据分类框架.二是参考监管数据分类.三是根据数据管理实践,结合企 ...

  9. JavaWeb学习笔记(狂神版)--- 第九节 Cookie与Session

    第九节 Cookie与Session 目录 第九节 Cookie与Session 9.1 会话 9.2 Cookie 9.3 Session 9.1 会话 会话:用户打开一个浏览器,点了很多超链接,访 ...

最新文章

  1. 企业网络推广专员浅析企业网络推广初期网站优化应重视的一些问题
  2. 2018最新版 手机号、验证码正则表达式 jq + 小程序
  3. Algorithm Master Road:算法的时间/空间复杂度
  4. import numpy as np_纪录27个NumPy操作
  5. 第一节:复习委托,并且通过委托的异步调用开启一个新线程和异步回调、异步等待
  6. 揭秘阿里云EB级大数据计算引擎MaxCompute
  7. MS SQL 2008认证考试大纲
  8. (6)verilog语言编写售货机
  9. vue.js computedmethod
  10. 从编写到上线带你搞定springboot博客01
  11. linux把虚拟机上的文件共享,[转]windows中vmware虚拟机中的Linux如何进行文件的共享...
  12. Vue---vue-cli 中的proxyTable解决开发环境中的跨域问题
  13. w3c subscribe
  14. PC端模拟微信/QQ/钉钉运行环境 解决 请在微信客户端打开链接 解决 2021
  15. c哩c哩歌是什么语言,c哩c哩是什么歌 音译歌词
  16. 玉米社:视频营销推广 标题描述优化技巧
  17. 三点法求点三维坐标实验
  18. wps里有project吗_wps 是否有project或者visol和outlook这种功能?现在不得不用office啊...
  19. 西南石油大学计算机考研人数,8所高校报考人数汇总,21考研这是要妥妥突破400万人的节奏?...
  20. 模拟定时自动关机编程程序源码

热门文章

  1. ArduPilot飞控AOCODARC-H7DUAL固件编译
  2. 【计算机网络】第三部分 数据链路层(17) 广域网SONET/SDH
  3. python - TypeError: combat(sume,sumu) missing 2 required positional arguments: sume,sumu
  4. 打印论文是单面还是双面?
  5. 《迅雷链精品课》第四课:区块链技术的发展趋势
  6. 播音气泡音是什么,气泡音怎么学,气泡音怎么练
  7. 影响微信公众号排名的因素
  8. 手机端上传图片出现“没有应用可执行操作”
  9. python热搜排行功能_用Python做一个微博热搜榜的语音播报
  10. App上架时,华为应用市场提示:在测试环境:Wi-Fi联网、EMUI11.0 ( P40),软件存在闪退。如何模拟EMUI11.0 ( P40)