PL-VINS线特征处理部分源码阅读

news2024/11/25 6:46:30

PL-VINS线特征处理部分源码阅读

  • 1 linefeature_tracker
  • 2 三角化
    • 单目三角化
    • 双目三角化
  • 3 后端优化
    • 线特征状态量
    • 重投影误差

本文主要阅读PL-VINS中引入线特征的代码实现,包括线特征表示方法(Plücker参数化方法、正交表示法)、前端线特征提取与匹配、三角化、后端优化等。

公式推导参考:SLAM中线特征的参数化和求导


1 linefeature_tracker

linefeature_tracker_node的整体结构跟feature_tracker_node基本一致,主要处理在readImage,用LSD作线特征提取,LBD做描述子匹配,这部分处理都是直接调用opencv接口实现的。

LSD线特征提取
设置了一些提取参数和阈值,特别是针对线段长度做了筛选。

Ptr<line_descriptor::LSDDetectorC> lsd_ = line_descriptor::LSDDetectorC::createLSDDetectorC();
    // lsd parameters
    line_descriptor::LSDDetectorC::LSDOptions opts;
    opts.refine       = 1;     //1     	The way found lines will be refined
    opts.scale        = 0.5;   //0.8   	The scale of the image that will be used to find the lines. Range (0..1].
    opts.sigma_scale  = 0.6;	//0.6  	Sigma for Gaussian filter. It is computed as sigma = _sigma_scale/_scale.
    opts.quant        = 2.0;	//2.0   Bound to the quantization error on the gradient norm
    opts.ang_th       = 22.5;	//22.5	Gradient angle tolerance in degrees
    opts.log_eps      = 1.0;	//0		Detection threshold: -log10(NFA) > log_eps. Used only when advance refinement is chosen
    opts.density_th   = 0.6;	//0.7	Minimal density of aligned region points in the enclosing rectangle.
    opts.n_bins       = 1024;	//1024 	Number of bins in pseudo-ordering of gradient modulus.
    double min_line_length = 0.125;  // Line segments shorter than that are rejected
    // opts.refine       = 1;
    // opts.scale        = 0.5;
    // opts.sigma_scale  = 0.6;
    // opts.quant        = 2.0;
    // opts.ang_th       = 22.5;
    // opts.log_eps      = 1.0;
    // opts.density_th   = 0.6;
    // opts.n_bins       = 1024;
    // double min_line_length = 0.125;
    opts.min_length   = min_line_length*(std::min(img.cols,img.rows));

    std::vector<KeyLine> lsd, keylsd;
	//void LSDDetectorC::detect( const std::vector<Mat>& images, std::vector<std::vector<KeyLine> >& keylines, int scale, int numOctaves, const std::vector<Mat>& masks ) const
    lsd_->detect( img, lsd, 2, 1, opts);

LBD描述子匹配筛选

做了一个匹配筛选,认为连续两帧中,同一个线段不会发生很大的移动,所以直接比较了两个端点的像素坐标和两个线段的夹角。

Point2f serr = line1.getStartPoint() - line2.getEndPoint()这一句可能是写错了,猜测是line2.getStartPoint()

if(curframe_->keylsd.size() > 0)
    {
        /* compute matches */
        TicToc t_match;
        std::vector<DMatch> lsd_matches;
        Ptr<BinaryDescriptorMatcher> bdm_;
        bdm_ = BinaryDescriptorMatcher::createBinaryDescriptorMatcher();
        bdm_->match(forwframe_->lbd_descr, curframe_->lbd_descr, lsd_matches);
//        ROS_INFO("lbd_macht costs: %fms", t_match.toc());
        sum_time += t_match.toc();
        mean_time = sum_time/frame_cnt;
        // ROS_INFO("line feature tracker mean costs: %fms", mean_time);
        /* select best matches */
        std::vector<DMatch> good_matches;
        std::vector<KeyLine> good_Keylines;
        good_matches.clear();
        for ( int i = 0; i < (int) lsd_matches.size(); i++ )
        {
            if( lsd_matches[i].distance < 30 ){

                DMatch mt = lsd_matches[i];
                KeyLine line1 =  forwframe_->keylsd[mt.queryIdx] ;
                KeyLine line2 =  curframe_->keylsd[mt.trainIdx] ;
                Point2f serr = line1.getStartPoint() - line2.getEndPoint();
                Point2f eerr = line1.getEndPoint() - line2.getEndPoint();
                // std::cout<<"11111111111111111 = "<<abs(line1.angle-line2.angle)<<std::endl;
                if((serr.dot(serr) < 200 * 200) && (eerr.dot(eerr) < 200 * 200)&&abs(line1.angle-line2.angle)<0.1)   // 线段在图像里不会跑得特别远
                    good_matches.push_back( lsd_matches[i] );
            }
        }


        vector< int > success_id;
        // std::cout << forwframe_->lineID.size() <<" " <<curframe_->lineID.size();
        for (int k = 0; k < good_matches.size(); ++k) {
            DMatch mt = good_matches[k];
            forwframe_->lineID[mt.queryIdx] = curframe_->lineID[mt.trainIdx];
            success_id.push_back(curframe_->lineID[mt.trainIdx]);
        }

坐标归一化

trackerData.undistortedLineEndPoints()操作后,得到当前帧下所有线特征起点和终点的归一化坐标(我感觉这个函数本意应该是要做去畸变的,结果代码里只做了像素坐标转归一化坐标,这样一来线特征不就没有去畸变吗?)

然后把id和归一化坐标pub出去

    pub_img = n.advertise<sensor_msgs::PointCloud>("linefeature", 1000);
    pub_match = n.advertise<sensor_msgs::Image>("linefeature_img",1000);

这版里面pub_match完全没用到啊,类比feature_tracker,猜测是留给双目显示左右目匹配用的。


2 三角化

三角化放在了里程计处理部分,一共有三种方案:

  • 点特征三角化(SFM)
  • 单目线特征三角化(SFM)
  • 双目线特征三角化
void Estimator::solveOdometry()
{
    if (frame_count < WINDOW_SIZE)
        return;
    if (solver_flag == NON_LINEAR)
    {
        TicToc t_tri;
        f_manager.triangulate(Ps, tic, ric);

        if(baseline_ > 0) //stereo
            f_manager.triangulateLine(baseline_);
        else
            f_manager.triangulateLine(Ps, tic, ric);

        ROS_DEBUG("triangulation costs %f", t_tri.toc());

        // optimization();

        onlyLineOpt();   // 三角化以后,优化一把
        optimizationwithLine();

#ifdef LINEINCAM
        LineBAincamera();
#else
//        LineBA();
#endif
    }
}

单目三角化

单目情况下线特征三角化同样是通过SFM实现。
通过ij两帧图像观测到的线段头尾端点,与当前帧相机坐标原点的连线,构造观测平面,两平面相交得到相机坐标系下的直线Lc。

分为下图两种情况:

在这里插入图片描述

情况1:imu_j == imu_i 直接计算跟光心连线构成的平面法向量

           if(imu_j == imu_i)   // 第一个观测是start frame 上
            {
                obsi = it_per_frame.lineobs;
                Eigen::Vector3d p1( obsi(0), obsi(1), 1 );
                Eigen::Vector3d p2( obsi(2), obsi(3), 1 );
                pii = pi_from_ppp(p1, p2,Vector3d( 0, 0, 0 ));
                ni = pii.head(3); ni.normalize();
                continue;
            }

情况2:imu_j != imu_i 算出第i帧图像坐标系下,第j帧图像的线段与光心构成的平面法向量

// 非start frame(其他帧)上的观测
            Eigen::Vector3d t1 = Ps[imu_j] + Rs[imu_j] * tic[0];
            Eigen::Matrix3d R1 = Rs[imu_j] * ric[0];

            Eigen::Vector3d t = R0.transpose() * (t1 - t0);   // tij
            Eigen::Matrix3d R = R0.transpose() * R1;          // Rij
            
            Eigen::Vector4d obsj_tmp = it_per_frame.lineobs;

            // plane pi from jth obs in ith camera frame
            Vector3d p3( obsj_tmp(0), obsj_tmp(1), 1 );
            Vector3d p4( obsj_tmp(2), obsj_tmp(3), 1 );
            p3 = R * p3 + t;
            p4 = R * p4 + t;
            Vector4d pij = pi_from_ppp(p3, p4,t);
            Eigen::Vector3d nj = pij.head(3); nj.normalize(); 

两平面相交,得到相机坐标系下直线的普朗克表示
在这里插入图片描述

Vector6d plk = pipi_plk( pii, pij );
Vector3d n = plk.head(3);
Vector3d v = plk.tail(3);
it_per_id.line_plucker = plk;  // plk in camera frame
it_per_id.is_triangulation = true;

双目三角化

双目更简单,把线段与左右目构成的平面相交。

在这里插入图片描述
来自论文《Building a 3-D Line-Based Map Using Stereo SLAM》

   // plane pi from ith left obs in ith left camera frame
        Vector3d p1( lineobs_l(0), lineobs_l(1), 1 );
        Vector3d p2( lineobs_l(2), lineobs_l(3), 1 );
        Vector4d pii = pi_from_ppp(p1, p2,Vector3d( 0, 0, 0 ));

        // plane pi from ith right obs in ith left camera frame
        Vector3d p3( lineobs_r(0) + baseline, lineobs_r(1), 1 );
        Vector3d p4( lineobs_r(2) + baseline, lineobs_r(3), 1 );
        Vector4d pij = pi_from_ppp(p3, p4,Vector3d(baseline, 0, 0));

        Vector6d plk = pipi_plk( pii, pij );
        Vector3d n = plk.head(3);
        Vector3d v = plk.tail(3);

3 后端优化

做完三角化之后,先做了一次线特征的优化onlyLineOpt(),然后做了滑窗整体的优化optimizationwithLine()

  • onlyLineOpt() 固定pose, 只优化line
  • optimizationwithLine() 优化pose、imu、feature(points and lines)

线特征状态量

在优化部分,作者把线特征从普朗克表示(6x1)转为正交表示(4x1),在plk_to_orth 实现.
这样做主要是因为直线的自由度是4,而普朗克表示是冗余的。


引用自论文《PLS-VIO_Stereo_Vision-inertial_Odometry_Based_on_Point_and_Line_Features》

在这里插入图片描述

Vector4d plk_to_orth(Vector6d plk)
{
    Vector4d orth;
    Vector3d n = plk.head(3);
    Vector3d v = plk.tail(3);

    Vector3d u1 = n/n.norm();
    Vector3d u2 = v/v.norm();
    Vector3d u3 = u1.cross(u2);

    // todo:: use SO3
    orth[0] = atan2( u2(2),u3(2) );
    orth[1] = asin( -u1(2) );
    orth[2] = atan2( u1(1),u1(0) );

    Vector2d w( n.norm(), v.norm() );
    w = w/w.norm();
    orth[3] = asin( w(1) );

    return orth;

}

重投影误差

这里的重投影误差是把世界坐标系下的直线投影到归一化平面上,然后计算之前提取的线段两端点坐标到直线投影的距离,理想情况下是0。

注意,重投影误差是在归一化平面进行的,与相机内参无关。

ceres::LocalParameterization *local_parameterization_line = new LineOrthParameterization();
        problem.AddParameterBlock( para_LineFeature[linefeature_index], SIZE_LINE, local_parameterization_line);  // p,q

        int imu_i = it_per_id.start_frame,imu_j = imu_i - 1;
        for (auto &it_per_frame : it_per_id.linefeature_per_frame)
        {
            imu_j++;
            if (imu_i == imu_j)
            {
                //continue;
            }
            Vector4d obs = it_per_frame.lineobs;                          // 在第j帧图像上的观测
            lineProjectionFactor *f = new lineProjectionFactor(obs);     // 特征重投影误差
            problem.AddResidualBlock(f, loss_function,
                                     para_Pose[imu_j],
                                     para_Ex_Pose[0],
                                     para_LineFeature[linefeature_index]);
            line_m_cnt++;
        }

在这里插入图片描述

重投影误差计算过程:

  1. 正交表示法→普朗克表示法
  2. 世界坐标系→b系→相机系→相机归一化坐标系
  3. 根据点到直线距离公式计算残差项

论文《PL-VIO: Tightly-Coupled Monocular Visual–Inertial Odometry Using Point and Line Features》中提到,当直线从相机系投影到归一化坐标系下,投影参数K是单位阵

如果是投影到像素坐标系,就采用下面这个投影矩阵。

在这里插入图片描述

这里的[l1, l2, l3]T,就是直线参数abc,然后带入点到直线距离公式:

在这里插入图片描述
论文写的线段中点,代码用的起始和末尾俩端点,没啥区别。

bool lineProjectionFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{
    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 tic(parameters[1][0], parameters[1][1], parameters[1][2]);
    Eigen::Quaterniond qic(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);

    Eigen::Vector4d line_orth( parameters[2][0],parameters[2][1],parameters[2][2],parameters[2][3]);
    Vector6d line_w = orth_to_plk(line_orth);

    Eigen::Matrix3d Rwb(Qi);
    Eigen::Vector3d twb(Pi);
    Vector6d line_b = plk_from_pose(line_w, Rwb, twb);
    //std::cout << line_b.norm() <<"\n";
    Eigen::Matrix3d Rbc(qic);
    Eigen::Vector3d tbc(tic);
    Vector6d line_c = plk_from_pose(line_b, Rbc, tbc);

    // 直线的投影矩阵K为单位阵
    Eigen::Vector3d nc = line_c.head(3);
    double l_norm = nc(0) * nc(0) + nc(1) * nc(1);
    double l_sqrtnorm = sqrt( l_norm );
    double l_trinorm = l_norm * l_sqrtnorm;

    double e1 = obs_i(0) * nc(0) + obs_i(1) * nc(1) + nc(2);
    double e2 = obs_i(2) * nc(0) + obs_i(3) * nc(1) + nc(2);
    Eigen::Map<Eigen::Vector2d> residual(residuals);
    residual(0) = e1/l_sqrtnorm;
    residual(1) = e2/l_sqrtnorm;

//    std::cout <<"---- sqrt_info: ------"<< sqrt_info << std::endl;
//    sqrt_info.setIdentity();
    residual = sqrt_info * residual;
    //std::cout << residual <<"\n";
    if (jacobians)
    {

        Eigen::Matrix<double, 2, 3> jaco_e_l(2, 3);
        jaco_e_l << (obs_i(0)/l_sqrtnorm - nc(0) * e1 / l_trinorm ), (obs_i(1)/l_sqrtnorm - nc(1) * e1 / l_trinorm ), 1.0/l_sqrtnorm,
                (obs_i(2)/l_sqrtnorm - nc(0) * e2 / l_trinorm ), (obs_i(3)/l_sqrtnorm - nc(1) * e2 / l_trinorm ), 1.0/l_sqrtnorm;

        jaco_e_l = sqrt_info * jaco_e_l;

        Eigen::Matrix<double, 3, 6> jaco_l_Lc(3, 6);
        jaco_l_Lc.setZero();
        jaco_l_Lc.block(0,0,3,3) = Eigen::Matrix3d::Identity();

        Eigen::Matrix<double, 2, 6> jaco_e_Lc;
        jaco_e_Lc = jaco_e_l * jaco_l_Lc;
        //std::cout <<jaco_e_Lc<<"\n\n";
        //std::cout << "jacobian_calculator:" << std::endl;
        if (jacobians[0])
        {
            //std::cout <<"jacobian_pose_i"<<"\n";
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);

            Matrix6d invTbc;
            invTbc << Rbc.transpose(), -Rbc.transpose()*skew_symmetric(tbc),
                    Eigen::Matrix3d::Zero(),  Rbc.transpose();

            Vector3d nw = line_w.head(3);
            Vector3d dw = line_w.tail(3);
            Eigen::Matrix<double, 6, 6> jaco_Lc_pose;
            jaco_Lc_pose.setZero();
            jaco_Lc_pose.block(0,0,3,3) = Rwb.transpose() * skew_symmetric(dw);   // Lc_t
            jaco_Lc_pose.block(0,3,3,3) = skew_symmetric( Rwb.transpose() * (nw + skew_symmetric(dw) * twb) );  // Lc_theta
            jaco_Lc_pose.block(3,3,3,3) = skew_symmetric( Rwb.transpose() * dw);

            jaco_Lc_pose = invTbc * jaco_Lc_pose;
            //std::cout <<invTbc<<"\n"<<jaco_Lc_pose<<"\n\n";

            jacobian_pose_i.leftCols<6>() = jaco_e_Lc * jaco_Lc_pose;

            //std::cout <<jacobian_pose_i<<"\n\n";

            jacobian_pose_i.rightCols<1>().setZero();            //最后一列设成0
        }

        if (jacobians[1])
        {

            //std::cout <<"jacobian_ex_pose"<<"\n";
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_ex_pose(jacobians[1]);

            Vector3d nb = line_b.head(3);
            Vector3d db = line_b.tail(3);
            Eigen::Matrix<double, 6, 6> jaco_Lc_ex;
            jaco_Lc_ex.setZero();
            jaco_Lc_ex.block(0,0,3,3) = Rbc.transpose() * skew_symmetric(db);   // Lc_t
            jaco_Lc_ex.block(0,3,3,3) = skew_symmetric( Rbc.transpose() * (nb + skew_symmetric(db) * tbc) );  // Lc_theta
            jaco_Lc_ex.block(3,3,3,3) = skew_symmetric( Rbc.transpose() * db);

            jacobian_ex_pose.leftCols<6>() = jaco_e_Lc * jaco_Lc_ex;
            jacobian_ex_pose.rightCols<1>().setZero();
        }
        if (jacobians[2])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 4, Eigen::RowMajor>> jacobian_lineOrth(jacobians[2]);

            Eigen::Matrix3d Rwc = Rwb * Rbc;
            Eigen::Vector3d twc = Rwb * tbc + twb;
            Matrix6d invTwc;
            invTwc << Rwc.transpose(), -Rwc.transpose() * skew_symmetric(twc),
                    Eigen::Matrix3d::Zero(),  Rwc.transpose();
            //std::cout<<invTwc<<"\n";

            Vector3d nw = line_w.head(3);
            Vector3d vw = line_w.tail(3);
            Vector3d u1 = nw/nw.norm();
            Vector3d u2 = vw/vw.norm();
            Vector3d u3 = u1.cross(u2);
            Vector2d w( nw.norm(), vw.norm() );
            w = w/w.norm();

            Eigen::Matrix<double, 6, 4> jaco_Lw_orth;
            jaco_Lw_orth.setZero();
            jaco_Lw_orth.block(3,0,3,1) = w[1] * u3;
            jaco_Lw_orth.block(0,1,3,1) = -w[0] * u3;
            jaco_Lw_orth.block(0,2,3,1) = w(0) * u2;
            jaco_Lw_orth.block(3,2,3,1) = -w(1) * u1;
            jaco_Lw_orth.block(0,3,3,1) = -w(1) * u1;
            jaco_Lw_orth.block(3,3,3,1) = w(0) * u2;

            //std::cout<<jaco_Lw_orth<<"\n";

            jacobian_lineOrth = jaco_e_Lc * invTwc * jaco_Lw_orth;
        }

    }

这里还有一个要留意的点,在计算点到直线距离的时候,直线abc参数直接就是法向量参数

光心与相机坐标系下的线特征两端连线,构成一个平面并且得到这个平面的法向量。那为什么这个法向量就是归一化平面上投影直线的参数呢?

法向量n=[n1, n2, n3],对于投影直线上的一个点(x, y, z), 这个点位于归一化平面上z=1,同时位于法向量所在平面上,所以就有 n1x+n2y+n3=0,对应了直线表达式 ax+by+c=0。

这也解释了为什么投影矩阵为单位阵,它的投影过程只与法向量有关,在这个过程中法向量并不发生变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/424371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

遥感、GIS及GPS在土壤空间数据分析、适应性评价、制图及土壤普查中的应用

摸清我国当前土壤质量与完善土壤类型&#xff0c;可以为守住耕地红线、保护生态环境、优化农业生产布局、推进农业高质量发展奠定坚实基础&#xff0c;为此&#xff0c;2022年初国务院印发了《关于开展第三次全国土壤普查的通知》&#xff0c;决定自2022年起开展第三次全国土壤…

微信支付,JSAPI支付,APP支付,H5支付,Native支付,小程序支付功能详情以及回调处理

一.支付相关文档地址支付wiki&#xff1a;https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml支付api: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/index.shtml开发工具包(SDK)下载&#xff1a;https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay6_0.shtm…

靶机精讲之CTF4

主机发现 靶机193 端口扫描 服务扫描 80&#xff0c;25&#xff08;明确版本&#xff09;攻击面更大 web渗透 blog是交互式的程序 发现index可进行手动爆破&#xff08;地址包含&#xff09; http://192.168.10.193/index.html?page../../../../../../../../etc/passwd 无发…

雨水情测报系统+智慧水库大坝安全监测系统

解决方案 雨水情测报系统智慧水库大坝安全监测系统&#xff0c;系统主要由降雨量监测站、水库水位监测站、大坝安全监测中的渗流量、渗流压力和变形监测站及视频和图像监测站等站点组成&#xff0c;同时建立规范、统一的监测平台&#xff0c;集数据传输、信息共享、数据储存于…

Git简单使用~下载、安装、命令行使用、IDEA使用

文章目录一、Git下载二、Git安装三、命令行操作四、IDEA使用gitee4. 查看Gitee仓库一、Git下载 官网下载地址&#xff1a;Git (git-scm.com) 点击"Download for Windows"&#xff0c;跳转至详细下载页面。 以Windows64位安装版为例&#xff0c;点击"64-bit…

代码随想录算法训练营第42天| 416. 分割等和子集

代码随想录算法训练营第42天| 416. 分割等和子集416. 分割等和子集416. 分割等和子集 题目链接&#xff1a;416. 分割等和子集&#xff0c;难度&#xff1a;中等 【实现代码】 class Solution { public:bool canPartition(vector<int>& nums) {int sum 0;for (int…

抢鲜发布:Flutter 3.7更新详解

本文首发自「慕课网」(imooc.com)&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;CrazyCodeBoy|慕课网讲师 新年伊始&#xff0c;由 Flutter 3.7 正式版来「打头阵」&#xff01;我们与整个…

AI时代,重新理解阿里云

如果说&#xff0c;在数字化时代&#xff0c;阿里云给外界的标签是基于算力、数据等要素的基建角色&#xff0c;那么&#xff0c;在如今的智能化时代&#xff0c;基于自身强大的云计算能力和长期以往的AI技术积累&#xff0c;它的这种底座底色显然再一次被夯实、彰显。 作者|皮…

【Python_Scrapy学习笔记(六)】Scrapy框架基本使用流程

Scrapy框架基本使用流程 前言 本文中介绍 Scrapy 框架的基本使用流程&#xff0c;并以抓取汽车之家二手车数据为例进行讲解。 正文 1、Scrapy框架基本使用流程 创建爬虫项目&#xff1a;scrapy startprojecct 项目名 cd到项目文件夹&#xff1a;cd 项目名 创建爬虫文件&a…

PCB模块化设计06——HDMI接口PCB布局布线设计规范

目录PCB模块化设计06——HDMI接口PCB布局布线设计规范1、HDMI接口的定义2、HDMI管脚定义&#xff08;A型为例&#xff09;3、HDMI接口PCB布局要求4、HDMI接口布线要求PCB模块化设计06——HDMI接口PCB布局布线设计规范 1、HDMI接口的定义 高清晰度多媒体接口&#xff08;英文&…

Jina AI 创始人肖涵博士:揭秘 Auto-GPT 喧嚣背后的残酷真相

Auto-GPT 究竟是一个开创性的项目&#xff0c;还是一个被过度炒作的 AI 实验&#xff1f;本文为我们揭开了喧嚣背后的真相&#xff0c;并揭示了 Auto-GPT 不适合实际应用的生产局限性。 背景介绍 这两天&#xff0c;Auto-GPT&#xff0c;一款让最强语言模型 GPT-4 能够自主完成…

winform从入门到精通

环境 开发工具 visual studio 2019 16.11 community 基础框架 framework4.8 .net5需要开发工具小版本16.8以上 winform开发入门 windows桌面端应用开发框架 https://github.com/dotnet/winforms c#基础 partial class 创建项目 项目结构 引用&#xff1a;所依赖的系统库 …

编写猫咪相册应用 HTML

文章目录1. 标题元素标签2. p元素用于在网站上创建一段文本3. 注释4. 页面主要部分标识标签5. 通过使用img元素来为你的网站添加图片6. 使用锚点元素(a)链接到另一个页面7. 使用 section 元素将照片内容与未来的内容分开8. 无序列表(ul)元素&#xff0c;列表项(li)元素在列表中…

【C++11】final与override关键字和类的新功能

目录 一、final与override关键字 1.1 final 1.2 override 二、类的新功能 2.1 默认成员函数 2.2 类成员变量初始化 2.3 default关键字 2.4 delete关键字 注意&#xff1a;C专栏的所有测试代码都是在 vs2019 的环境下编译运行的 一、final与override关键字 这两个关键…

C++基础入门

C基础入门1.C认识1.1 第一个C程序1.1.1 创建项目1.1.2 创建文件1.1.3 编写代码1.1.4 运行程序1.3 变量1.4 常量1.5 关键字1.6 标识符命名规则2 数据类型2.1 整型2.2 sizeof关键字2.3 实型&#xff08;浮点型&#xff09;2.4 字符型2.5 转义字符2.6 字符串型2.7 布尔类型 bool2.…

Session、Cookie和Token

Session、Cookie和Token 参考&#xff1a;Session、Cookie、Token 【浅谈三者之间的那点事】 HTTP协议是一种无状态协议&#xff0c;即每次服务端接收到客户端请求时&#xff0c;都是一个全新的请求&#xff0c;服务器并不知道客户端的历史请求记录&#xff1b;Session和Cooki…

统信UOS + Windows双系统安装教程

全文导读&#xff1a;本文主要介绍了AMD架构下&#xff08;Intel/amd/兆芯/海光&#xff09;的机器同时安装Windows系统UOS系统的方法。 准备环境 1、下载好UOS系统镜像&#xff08;AMD64&#xff09;&#xff0c;下载地址&#xff1a;https://www.chinauos.com/resource/down…

第6章 接口、lambda表达式与内部类

文章目录Java与C异Java与C同接口接口概念接口与抽象类静态和私有方法默认方法解决默认方法冲突接口与回调&#xff08;不明白&#xff09;Comparator接口为什么有比较器Comparator接口实现步骤示例问题对象克隆为什么克隆浅拷贝&#xff1a;Object.clone方法的问题深拷贝数组的…

Firefox浏览器-渗透测试插件推荐

在日常工作中可能需要一些浏览器插件辅助我们做工作 下面是比较好的&#xff0c;当然不一定对你有用&#xff0c;找到适合自己的即可 FoxyProxy FoxyProxy是一个高级的代理管理工具&#xff0c;它完全替代了Firefox有限的代理功能。它提供比SwitchProxy、ProxyButton、 Quick…

3-5年以上的功能测试如何进阶自动化?【附学习路线】

做为功能测试人员来讲&#xff0c;从发展方向上可分两个方面&#xff1a; 1、业务流程方向 2、专业技能方向。 当确定好方向后&#xff0c;接下来就是如何达到了。(文末自动化测试学习资料分享) 一、业务流程方向 1、熟悉底层的业务 作为功能测试工程师来讲&#xff0c;了解…