ardupilot开发 --- 机载(边缘)计算机-VISP高阶 篇

让我再看你一眼从南到北

  • 0. 基础
  • 1. 视觉伺服
    • 1.1 视觉伺服基础
      • 1.1.1 基本理论
      • 1.1.2 代码解析(tutorial-ibvs-4pts.cpp):
    • 1.2 基于图像处理的视觉伺服

0. 基础

基础知识点请参考基础篇。

1. 视觉伺服

参考:Visual servoing

1.1 视觉伺服基础

参考1:Tutorial: Image-based visual servo
参考2:关于视觉伺服控制的一些基本概念、原理、公式推导(建议观看)

1.1.1 基本理论

  • 基于图像的视觉伺服(IBVS):image-based visual servo
    基于图像点特征的视觉伺服;
    原理推导

  • 基于位置的视觉伺服(PBVS):position-based visual servo

  • 视觉伺服控制系统
    以【实现:搭载深度相机的无人机在竖立的二维码正前方保持悬停】为例。
    在这里插入图片描述

    • 被控对象:无人机、相机、二维码等组成的广义对象。

    • 系统输出: 视觉特征(visual features)

    • 输出期望值:视觉特征期望值。

    • 系统误差:视觉特征误差。

    • 控制输入:相机坐标系下相机的速度向量Vc=[u,w],其中u为线速度,w角速度。其实就是控制飞机的飞行速度(因为相机与飞机是固连的)。

    • 控制律:如下。理论推导请参考,或直接搜索视觉伺服。
      在这里插入图片描述
      其中lambda是控制器的常数增益,一般设为0.5。

    • 相互作用矩阵Lx的表达式为:
      在这里插入图片描述
      其中 Lx 为交互矩阵(interaction matrix), 又称特征雅可比矩阵(feature Jacobian)。
      Lx一般不可以完全得到,可通过估计的方式获得:视觉伺服学习笔记——基本方法

    • 分析
      众所周知,在控制系统中,如何定义系统输出、输出期望值、系统误差 是跟我们的控制目标有关的。
      在本例子中,要实现的控制目标是:保持飞机(也就是相机)悬停在二维码正前方设定距离处。
      如果将该二维码看做一个3D点 p,那么对于相机而言,只要使得点 p 在相机坐标系下的坐标保持在一个固定值如【0, 0,L 】即可实现控制目标。
      这样一来,暂时可以把 p 点在相机坐标系下的坐标定义为控制系统的输出量,将二维码的中心定义为p点,那么【0, 0,L 】自然就是系统的输出量期望值,期望值与输出量之间的误差自然就是系统误差。
      但是对于2D视觉伺服控制系统而言,为了方便运算一般要把所有纲量统一到图像坐标系来做运算,分析如下:
      对于RGB-D相机:已知一个3D点 p 在相机坐标系下的坐标值为【X, Y, Z】;
      对于RGB相机:已知点 p 在像素坐标系中的坐标值(下标)为【u,v】;
      已知相机内参为 u0,v0,px,py ;
      那么,根据相机内参标定的原理公式可知,点 p 在图像坐标系中的坐标值的计算表达式如下:
      在这里插入图片描述
      点 p 在图像坐标系中的坐标值【x,y】就是最终要定义的控制系统输出量,把【0, 0,L 】代替【X, Y, Z】或把图像中心坐标代替【u,v】就可以计算得到控制系统期望值,那么系统误差就是两者之差了。
      在传统的视觉伺服控制系统中,一般把点 p (或其他合适的点) 在图像坐标系中的坐标值【x,y】定义为 视觉特征(visual features)但是这并不是定义视觉特征的唯一选择,只能要能有助于实现控制目标的量都可以定义为视觉特征
      到此就解释了为什么视觉特征为什么可以定义为视觉伺服系统的输出量了,也弄清了什么是视觉特征。
      对于本文而言,我们暂且把这些p点定义为视觉伺服系统的特征点,注意该概念与机器视觉中的特征点的不同之处!

  • 视觉伺服理论中的一些结论
    详细推导请参考:https://zhuanlan.zhihu.com/p/422634446 或 https://zhuanlan.zhihu.com/p/389903710

    • 在IBVS中,视觉特征一般用一系列合适的点的图像坐标系坐标来表示。

    • 以点坐标表征的视觉特征称为点视觉特征,被选作视觉特征的点称为特征点

    • eye-to-hand 和 eye-in-hand
      eye-to-hand:相机固定在一个地方,机械手的运动不会带着相机一起移动。
      eye-in-hand:相机安装在机械手上,随着机械手一起移动。

    • 视觉特征向量,例如:
      假设定义了n个视觉特征(选取n个点),视觉特征向量 s 的表达式为:

      s = [ p 1 ⋮ p n ] = [ x 1 y 1 ⋮ x n y n ] 2 n ∗ 1 ( 式 1 ) s=\begin{bmatrix} p_1 \\ \vdots \\ p_n \end{bmatrix}=\begin{bmatrix} x_1 \\y_1 \\\\ \vdots \\ x_n \\y_n \end{bmatrix}_{2n*1}{(式1)} s= p1pn = x1y1xnyn 2n1(1)x,y是特征点在图像坐标系下的坐标值,什么是图像坐标系请查阅:相机标定篇

    • 控制向量Vc
      V c = [ v c w c ] = [ v x v y v z w x w y w z ] 6 ∗ 1 ( 式 2 ) V_c=\begin{bmatrix} v_c \\ w_c \end{bmatrix}=\begin{bmatrix} v_x \\v_y \\v_z\\ w_x \\w_y \\w_z\\ \end{bmatrix}_{6*1}{(式2)} Vc=[vcwc]= vxvyvzwxwywz 61(2)其中,vc, wc 分别是线速度和角速度(相机系)。

    • 控制率
      V c = − λ L s + e       ( 式 3 ) V_c=-\lambda{L_s^+}{e}{\space\space\space\space\space}{(式3)} Vc=λLs+e     (3)其中: e = s e = [ x e 1 y e 1 ⋮ x e n y e n ] 2 n ∗ 1 ( 式 4 ) e=s_e=\begin{bmatrix} x_{e_1} \\y_{e_1} \\ \vdots \\ x_{e_n} \\y_{e_n} \end{bmatrix}_{2n*1}{(式4)} e=se= xe1ye1xenyen 2n1(4)
      L s = [ L 1 s ⋮ L n s ] 2 n ∗ 6 = [ [ − 1 / Z 1 0 x 1 / Z 1 x 1 y 1 − ( 1 + x 1 2 ) y 1 0 − 1 / Z 1 y 1 / Z 1 1 + y 1 2 − x 1 y 1 − x 1 ] ⋮ [ − 1 / Z n 0 x n / Z n x n y n − ( 1 + x n 2 ) y n 0 − 1 / Z n y n / Z n 1 + y n 2 − x n y n − x n ] ] 2 n ∗ 6 ( 式 5 ) {L_s}=\begin{bmatrix} L1_s \\ \vdots \\ Ln_s \end{bmatrix}_{2n*6}=\begin{bmatrix} \begin{bmatrix} -1/Z_1&0&x_1/Z_1&x_1y_1&-(1+x_1^2)&y_1\\ 0&-1/Z_1&y_1/Z_1&1+y_1^2&-x_1y_1&-x_1 \end{bmatrix}\\ \vdots \\ \begin{bmatrix} -1/Z_n&0&x_n/Z_n&x_ny_n&-(1+x_n^2)&y_n\\ 0&-1/Z_n&y_n/Z_n&1+y_n^2&-x_ny_n&-x_n \end{bmatrix}\\ \end{bmatrix}_{2n*6}{(式5)} Ls= L1sLns 2n6= [1/Z1001/Z1x1/Z1y1/Z1x1y11+y12(1+x12)x1y1y1x1][1/Zn001/Znxn/Znyn/Znxnyn1+yn2(1+xn2)xnynynxn] 2n6(5)
      L s + = [ … ] 6 ∗ 2 n    ( 式 6 ) {L_s^+}=\begin{bmatrix}\dots\end{bmatrix}_{6*2n}{\space\space}{(式6)} Ls+=[]62n  (6)Lx 的广义逆矩阵。
      很多文献中也把 Ls 记作 LeLx .

    • 通常存在4个不同的相机姿态使得e=0,即存在4个不可能区分的全局最优点。一般地,选择多于3个点,作为视觉特征向量。

    • 获取Lx的常用方法
      在这里插入图片描述

1.1.2 代码解析(tutorial-ibvs-4pts.cpp):

对于tutorial-ibvs-4pts.cpp例子而言,定义了4个视觉特征,即定义了4个跟踪点(上一节分析中提到的p点,或称视觉特征点)。

  • 定义相机的期望位置、初始位置。若以跟踪对象作为参考系(跟踪对象是不动的),则这两个位置量可以用坐标转换矩阵cdMo,cMo表示:

    vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0);
    vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50));
    

    cMo矩阵的含义请参考本章的第 1.4.1 小节。

  • 以【相机检测一个20cm*20cm矩形,让保持相机在矩形中心位置】为例。
    世界坐标系下,定义4个点,表示检测对象的四个角,在此例中,检测对象即20x20的矩形:

    vpPoint point[4] ;
    point[0].setWorldCoordinates(-0.1,-0.1, 0);
    point[1].setWorldCoordinates( 0.1,-0.1, 0);
    point[2].setWorldCoordinates( 0.1, 0.1, 0);
    point[3].setWorldCoordinates(-0.1, 0.1, 0);
    
  • 定义视觉伺服器:
    vpServo task ;

  • 配置视觉伺服方式:eye in hand 模式
    task.setServo(vpServo::EYEINHAND_CAMERA);

  • 定义4个视觉特征
    根据上一小节的分析可知,视觉特征就是被跟踪的点的图像坐标系坐标值。本例中,将矩形的4个角点设定为被跟踪的点,那么就会有4个视觉特征。表征视觉特征的4个点的坐标被保存在变量vpFeaturePoint p[4], pd[4];中,其中 p[4] 保存当前视频帧(图像帧)的视觉特征,pd[4] 保存视觉特征期望值

  • 计算视觉特征
    根据上一小节的分析可知,视觉特征是用跟踪的点的图像坐标系坐标值来表征的。

    • point[i].track(cdMo);
      将点 point[i] 的坐标转换到相机坐标系下,结果保存在 point[i] 的祖父类 vpTracker 的 cP 中;
      将点 point[i] 的坐标转换到图像坐标系下,结果保存在 point[i] 的祖父类 vpTracker 的 p 中;
      其中 point 的类型为 vpPoint,而 vpPoint 继承了vpForwardProjection,vpForwardProjection 继承了vpTracker,因此point 可以直接访问到 cP 和 p 。可知,point[i] 保存了第i点的世界系坐标、相机系坐标、图像系坐标。
    • vpFeatureBuilder::create(pd[i], point[i]);
      将点point[i]在图像系下的坐标 p 赋值给视觉特征变量 pd[i],create函数如下:
      void vpFeatureBuilder::create(vpFeaturePoint &s, const vpPoint &p)
      {
          s.set_x(p.get_x());
          s.set_y(p.get_y());
          s.set_Z(p.cP[2] / p.cP[3]);
      }
      
    • task.addFeature(p[i], pd[i]);
      将保存视觉特征的变量的地址记录到(拷贝到)task的 featureList 和 desiredFeatureList 中,addFeature函数如下:
      void vpServo::addFeature(vpBasicFeature &s_cur, vpBasicFeature &s_star, unsigned int select)
      {
        featureList.push_back(&s_cur);
        desiredFeatureList.push_back(&s_star);
        featureSelectionList.push_back(select);
      }
      
      因为拷贝的是地址,因此一次就够了,后续cMo更新后不需要再进行这步操作。
    • 更新 cMo
      在仿真中,是可以直接知道相机在世界坐标系中的坐标的,因此可以通过这个坐标算出cMo矩阵,如下。但在具体应用场景中cMo一般是通过目标检测算法得到的。
      robot.getPosition(wMc);
      cMo = wMc.inverse() * wMo;
      
    • 相机位置改变后 cMo 被更新,进而需要更新视觉特征:与上一步类似,这里不再赘叙。
      for (unsigned int i = 0 ; i < 4 ; i++) {
         point[i].track(cMo);
         vpFeatureBuilder::create(p[i], point[i]);
       }
      
      不需要再进行addFeature操作,因为featureList、desiredFeatureList 已经记录了视觉特征的引用。
  • 根据控制律公式,计算控制量Vc:vpColVector v = task.computeControlLaw();
    在这里插入图片描述
    在这里插入图片描述
    注意:控制律中Lx右上角的符号是求伪逆矩阵的意思。伪逆矩阵即广义逆矩阵。
    computeControlLaw 重点代码分析如下:

    • (一) 计算误差:computeError()
      根据featureList、desiredFeatureList计算系统误差,结果存入类vpServo 的三个变量中:
      class vpServo  {
      public:
      	vpColVector error; //保存所有视觉特征(点)的所有维度的误差
      	vpColVector s; //保存所有视觉特征(点)的所有维度的值
      	vpColVector sStar; //保存所有视觉特征(点)的所有维度的期望值
      }
      
      例如,定义了4个视觉特征,分别用4个点的坐标 [x1, y1, z1] ~ [x4, y4, z4]表示,保存在featureList中。
      那么 s,sStar,error 的最终尺寸大小都被定义为 12x1,其中12=3*4(4个点,每个点的坐标都是3个维度)。
      已知,featureList中的元素类型是vpBasicFeature ,类型vpBasicFeature 中有一个 vpColVector 类型的元素 s;
      已知,类 vpServo 中有三个类型为vpColVector 的成员:s,sStar,error。
      误差计算可以归纳为:
      • (1)对于视觉特征、视觉特征期望值,赋值过程的伪代码可以表示为:
        vpServo :: vpColVector s = featureList.at(i):: vpColVector s ;
        vpServo :: vpColVector sStar = desiredFeatureList.at(i):: vpColVector s ;
        实质上可以看作是将类vpBasicFeature中的成员s赋给类vpServo中的成员s.
      • (2)对于误差,赋值过程的伪代码可以表示为:
        vpServo :: vpColVector error = vpServo :: s - vpServo :: sStar ;
      • 例如:有4个视觉特征,分别用4个点的坐标 [x1, y1, z1] ~ [x4, y4, z4]表示,保存在featureList中。那么 s,sStar,error 的最终尺寸大小都被定义为 12x1,其中12=3*4(4个点,每个点的坐标都是3个维度)。s,sStar,error 的伪代码赋值逻辑如下:
        • s的赋值:
          vpServo :: s[0] = featureList.at(i) :: s[0] = x1 ;
          vpServo :: s[1] = featureList.at(i) :: s[1] = y1 ;
          vpServo :: s[2] = featureList.at(i) :: s[2] = z1 ;

          vpServo :: s[9] = featureList.at(i+3) :: s[0] = x4 ;
          vpServo :: s[10] = featureList.at(i+3) :: s[1] = y4 ;
          vpServo :: s[11] = featureList.at(i+3) :: s[2] = z4 ;
        • sStar 同理。
        • error 的赋值:
          vpServo :: error[0] = vpServo :: s[0] - vpServo :: sStar[0] ; // x1的误差
          vpServo :: error[1] = vpServo :: s[1] - vpServo :: sStar[1] ; // y1的误差
          vpServo :: error[2] = vpServo :: s[2] - vpServo :: sStar[2] ; // z1的误差

          vpServo :: error[9] = vpServo :: s[9] - vpServo :: sStar[9] ; // x4的误差
          vpServo :: error[10] = vpServo :: s[10] - vpServo :: sStar[10] ; // y4的误差
          vpServo :: error[11] = vpServo :: s[11] - vpServo :: sStar[11] ; // z4的误差
      • vpServo.cpp 中计算误差vpServo::error的几个重要的函数盘点:
        (1) 从vpBasicFeature类中提取vpColVector s :
        vectTmp = current_s -> get_s( ) 
        
        (2) 复制 vpBasicFeature :: s 到 vpServo :: s
        s[cursorS++] = vectTmp[k]; //其中s[0],s[1],s[2]分别是表征视觉特征的坐标值xyz
        sStar[cursorSStar++] = vectTmp[k];
        
        (3) 计算误差
        vectTmp = current_s->error(*desired_s, select);
        error[cursorError++] = vectTmp[k];
        
        //! Compute the error between two visual features from a subset of the
        //! possible features.
        vpColVector vpBasicFeature::error(const vpBasicFeature &s_star, unsigned int select)
        {
          vpColVector e(0), eLine(1);
          if (dim_s <= 31) {
            for (unsigned int i = 0; i < dim_s; ++i) {
              if (FEATURE_LINE[i] & select) {
                eLine[0] = s[i] - s_star[i];
                e.stack(eLine);
                // std::cout << "dim_s <= 31"<<std::endl;
              }
            }
          } else {
            e.resize(dim_s);
            vpColVector sd = s_star.get_s();
            e = s - sd;
          }
        
          return e;
        }
        
    • (二) 计算相互作用矩阵 Ls:task.computeInteractionMatrix()
      Jacobian矩阵Ls一般不能直接获取,因为表达式中需要深度 Z 值。一般 Ls 的获取方式有以下几种:
      在这里插入图片描述
      vpServo类中对应以下几种计算Lx的类型:current, mean, desired, user,如:
        // Type of the interaction matrix (current, mean, desired, user)
        vpServoIteractionMatrixType interactionMatrixType;
      
      本例中的interactionMatrixType类型是CURRENT。
      计算 Ls 的伪代码逻辑如下:
      • 计算结果保存在vpServo::L中。
      • 理论公式如(式5)。
      • 对于视觉伺服中的 eye in hand 和 eye to hand 问题都有:
        cVa = cVe;
        aJe = eJe;
      • computeInteractionMatrix();
      • 对于用CURRENT方法计算Ls
          for (it = featureList.begin() ... ) {
            matrixTmp = (*it)->interaction(*it_select);
            /* Copy the temporarily matrix into L. */
            for (unsigned int k = 0; k < rowMatrixTmp; ++k, ++cursorL) {
              for (unsigned int j = 0; j < colMatrixTmp; ++j) {
                L[cursorL][j] = matrixTmp[k][j];
              }
            }
          }
        
        vpMatrix vpFeaturePoint::interaction(unsigned int select)
        {
          vpMatrix L;
          double x_ = get_x();
          double y_ = get_y();
          double Z_ = get_Z();
          if (vpFeaturePoint::selectX() & select) {
            vpMatrix Lx(1, 6);
            Lx = 0;
            Lx[0][0] = -1 / Z_;
            Lx[0][1] = 0;
            Lx[0][2] = x_ / Z_;
            Lx[0][3] = x_ * y_;
            Lx[0][4] = -(1 + x_ * x_);
            Lx[0][5] = y_;
            L = vpMatrix::stack(L, Lx);
          }
          if (vpFeaturePoint::selectY() & select) {
            vpMatrix Ly(1, 6);
            Ly = 0;
            Ly[0][0] = 0;
            Ly[0][1] = -1 / Z_;
            Ly[0][2] = y_ / Z_;
            Ly[0][3] = 1 + y_ * y_;
            Ly[0][4] = -x_ * y_;
            Ly[0][5] = -x_;
            L = vpMatrix::stack(L, Ly);
          }
          return L;
        }
        
        由以上伪代码可知,matrixTmp的表达式即:
        L s ( i ) = [ − 1 / Z i 0 x i / Z i x i y i − ( 1 + x i 2 ) y i 0 − 1 / Z i y i / Z i 1 + y i 2 − x i y i − x i ] 2 ∗ 6 {L_s(i)}=\begin{bmatrix} -1/Z_i&0&x_i/Z_i&x_iy_i&-(1+x_i^2)&y_i\\ 0&-1/Z_i&y_i/Z_i&1+y_i^2&-x_iy_i&-x_i \end{bmatrix}_{2*6} Ls(i)=[1/Zi001/Zixi/Ziyi/Zixiyi1+yi2(1+xi2)xiyiyixi]26
        vpServo::L的最终形式即:
        L s = [ L s ( 1 ) ⋮ L s ( n ) ] 2 n ∗ 6 {L_s}=\begin{bmatrix} L_s(1) \\ \vdots \\ L_s(n) \end{bmatrix}_{2n*6} Ls= Ls(1)Ls(n) 2n6
    • (二) 计算执行器终端固连坐标系下的雅可比矩阵 J1
      原因:因为控制量是要作用在执行器终端固连坐标系中的,例如无人机视觉伺服则要把控制量作用在飞机机体坐标系,然而通过控制 Vc= - lambda * Ls * e 计算得到的控制量是相机坐标系下的控制量,因此要把控制量转换到执行器终端固连坐标系中,所以要计算J1.
      赋值伪代码:
      • 计算结果保存在vpServo::J1中。
      • 表达式:J1 = Ls * cVa * aJe
        cVa、aJe 矩阵的含义请查看下一小节。
      • 代码:
          // compute  task Jacobian
          if (iscJcIdentity)
            J1 = L * cVa * aJe;
          else
            J1 = L * cJc * cVa * aJe;
        
    • (三) 计算执行器终端固连坐标系的最终控制量 vpColVector v
      e1 = J1p * error; // primary task
      e = -lambda(e1) * e1;
      return e;
      
  • 解析 vpServo 类

    • 什么是视觉特征?
      在IBVS中以图像系下某些点的坐标来定义视觉特征向量。
    • 视觉特征 向量
      vpColVector s;
      2D 矩阵,尺寸:2n x 1 ,n是特征点个数
      computeError()或computeControlLaw()执行后将s更新。
    • 视觉特征期望值 向量
      vpColVector sStar;
      2D 矩阵,尺寸:2n x 1 ,n是特征点个数
      computeError()或computeControlLaw()执行后将sStar更新。
    • 视觉特征误差 向量
      vpColVector error;
      2D 矩阵,尺寸:2n x 1 ,n是特征点个数
      computeError()或computeControlLaw()执行后将error更新。
    • 视觉特征列表
      std::list<vpBasicFeature *> featureList;
      所有视觉特征变量的地址列表,例如定义了4个点作为视觉特征则list就会有4个元素,一个元素就是一个视觉特征变量的地址。
      注意与s的区别。
    • 视觉特征期望值列表
      std::list<vpBasicFeature *> desiredFeatureList;
      与featureList的含义同理。
      元素数量与featureList同。
      注意与sStar的区别。
    • 视觉特征使能列表
      std::list featureSelectionList;
      例如定义了4个视觉特征只使用了其中3个,甚至可以使能每个视觉特征的某个维度,例如表征视觉特征的点坐标[x,y,z] 只让xy起作用参与有关视觉特征的运算。
      例如0xFFFF则启用,否则禁用。
      元素数量与featureList同。
    • 单位矩阵
      vpMatrix I;
    • 投影算子
      vpMatrix WpW;
    • 投影算子: I - WpW
      vpMatrix I_WpW;
    • 相互作用矩阵矩阵 Ls,也叫雅可比矩阵(Jacobian)
      vpMatrix L;
      表达式如(式5),尺寸:2n x 6
      注意与 vpServo::J1 的区别!!
    • Task Jacobian
      vpMatrix J1;
      具体情形下的雅可比矩阵坐标变换。
      注意与Jacobian矩阵( 即 L )的区别!
      表达式:
      J 1 = L ( c V a ) ( a J e ) J_1 = L ({^c}V_a) ({^a}J_e) J1=L(cVa)(aJe)
      cVa,aJe的表达式往下看便知。在本例中cVa与cVe同,aJe与eJe同。
    • cJc 矩阵
      vpMatrix cJc;
      在相机系中vc、wc的哪些自由度是可控的。
      对角矩阵,若vc、wc所有维度都可控则cJc是单位阵。
    • cVe 矩阵
      vpVelocityTwistMatrix cVe;
      相机坐标系 与 执行器末端固连坐标系之间的转换关系矩阵。
    • cVf 矩阵
      vpVelocityTwistMatrix cVf;
      相机坐标系 与 robot base frame 之间的转换关系矩阵。
    • fVe 矩阵
      robot base frame 与 执行器末端固连坐标系之间的转换关系矩阵。
    • eJe 矩阵
      vpMatrix eJe;
      Jacobian矩阵在执行器末端固连坐标系中的表达式。
    • fJe 矩阵
      vpMatrix fJe;
      Jacobian矩阵在robot base frame 中的表达式。
    • 最终控制量
      vpColVector e;
      表达式:
      e = e 1 + ( I − J 1 + J 1 ) e 2 e = e_1 + (I-{J_1}^{+} J_1) e_2 e=e1+(IJ1+J1)e2
    • e1
      vpColVector e1;
      表达式:
      e 1 = J 1 + ( s − s ∗ ) e_1 = {J_1}^{+}(s-s*) e1=J1+(ss)
    • 执行器终端坐标系、robot base frame 说明
      在这里插入图片描述
  • 结果分析
    在这里插入图片描述
    左图:4个特征点在图像坐标系中的位置轨迹。
    右图:世界坐标系下,相机光心的移动轨迹。

1.2 基于图像处理的视觉伺服

在仿真例程tutorial-ibvs-4pts.cpp中,cMo是通过相机的世界坐标系坐标直接计算得到的,但在真实应用场景中,cMo应该是通过目标检测算法得到的。以Blob检测算法为例,使用Blob检测来得到特征点的cMo矩阵然后应用在视觉伺服控制系统中。
例程:tutorial-ibvs-4pts-image-tracking.cpp
参考:IBVS simulation with image processing
结果:
在这里插入图片描述
例程:tutorial-ibvs-4pts-image-tracking.cpp.
代码分析:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

达芬奇调色:色彩理论入门

写在前面 整理一些达芬奇调色的笔记博文内容涉及&#xff1a; 一级调色是什么&#xff0c;以及 调色素材格式 log&#xff0c;raw&#xff0c;rec709 简单认知理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#…

全网最全搭建Electron项目的各种方式及错误解决

一、官方文档手动搭建 文档地址&#xff1a;快速入门 | Electron&#xff0c;按照文档步骤操作即可&#xff0c;项目只包含了Electron依赖&#xff0c;仅仅只是一个hello world展示。 二、github上拉取官网的electron-quick-start项目 electron-quick-start跟方式一创建的一…

MySQL数据库运维:运行监控及解决sql执行死锁问题

前言 在现代数据密集型应用程序的开发和部署中&#xff0c;MySQL数据库的运维是至关重要的环节之一。一个良好设计和维护的MySQL数据库系统可以确保数据的准确性、可靠性和高效的访问&#xff0c;从而支持业务的顺利运行。然而&#xff0c;随着业务规模的增长和复杂性增加&…

Spring 5源码学习

文章目录 一. 访问[spring官网], 找到Spring Framework&#xff0c;点击红色标记github仓库&#xff0c;下载对应的分支代码&#xff0c;本人下载5.1.x二. 安装gradle三. 调整spring-framework配置四. 开始编译五.导入idea 一. 访问[spring官网], 找到Spring Framework&#xf…

使用Python和wxPython下载视频封面

介绍&#xff1a; 在在线视频内容的世界中&#xff0c;是领先的平台。拥有数十亿的视频&#xff0c;拥有引人注目的封面图像非常重要&#xff0c;以吸引观众。在本博客文章中&#xff0c;我们将探讨如何使用Python和wxPython模块下载视频封面。我们将提供两个代码示例&#xff…

图像数据做并行规约时,如何确定共享内存和网格的大小

做并行规约时&#xff0c;如何确定共享内存和网格的大小 1、为什么要确定共享内存和网格大小2、共享内存大小定义3、网格大小 注&#xff1a;1、这里记录使用笔记&#xff0c;不对cuda的名词做解释&#xff0c;没有详细数学原理和代码。 2、环境&#xff1a;cuda8.0&#xff0c…

密码学 | Random Oracle 随机预言机

​ &#x1f951;原文&#xff1a;究竟什么才是随机预言机呢&#xff1f; - 玄星的回答 &#x1f951;答主指出&#xff1a; 英文维基明明对 随机预言机 给出了两个完全不同的理解&#xff0c;但这两个理解之间的连接词却是 “Stated differently”&#xff0c;即 “换句话说…

STM32通过ESP8266(MQTT)连接新版ONENET(2024/4/23)(保姆级教程)附运行结果

⏩ 大家好哇&#xff01;我是小光&#xff0c;想要成为系统架构师的嵌入式爱好者。 ⏩在各种嵌入式系统中我们经常会使用上位机去做显示&#xff0c;本文对STM32通过ESP8266连接最新版的ONENET做一个详细教程。 ⏩感谢你的阅读&#xff0c;不对的地方欢迎指正。 STM32通过ESP82…

【图说】VMware Ubuntu22.04 详细安装教程

前言 无论是从事 Linux 开发工作&#xff0c;还是希望电脑运行双系统&#xff0c;VMware 虚拟机都是我们日常工作不可或缺的工具。本章将会重点介绍 VMware 安装流程&#xff0c;以及在 VMware 上如何运行、使用 Ubuntu22.04 系统。 一、VMware 下载安装 1.1 VMware 官网下载…

如何查看西门子触摸屏的镜像版本?

如何查看西门子触摸屏的镜像版本? 当软件组态的设备版本和实际设备镜像之间版本不同时,那么在传输项目时就会出现兼容性冲突的提示。 镜像版本说明: 如何调整镜像版本(升级或降级)? 为了使用新功能以及提高面板的稳定性、可靠性和可用性,建议使用新的镜像版本。 一、 通…

目标检测算法是指什么?

一、目标检测算法是指什么&#xff1f; 目标检测算法是计算机视觉领域的一个重要分支&#xff0c;它旨在识别和定位图像中的目标对象。以下是目标检测算法的相关内容&#xff1a; 目标检测的核心问题&#xff1a;目标检测需要解决的两个核心问题是“目标是什么”和“目标在哪里…

【计算机网络】(三)物理层 - 通信基础

文章目录 【计算机网络】&#xff08;三&#xff09;物理层 - 通信基础前言3.1 物理层的基本概念3.2 数据通信的基础知识3.2.1 数据、信号、码元3.2.2 信源、信宿、信道3.2.3 编码、调制3.2.3.1 基带调制&#xff08;编码&#xff09;3.2.3.2 带通调制&#xff08;调制&#xf…

想搭建跨境电商网站?掌握这些源码关键点,助您轻松上线

随着全球化的发展和电子商务的兴盛&#xff0c;跨境电商已成为企业拓展国际市场的主要方式之一。然而&#xff0c;想要搭建一个成功的跨境电商网站并非易事&#xff0c;其中关键之一就是掌握跨境电商网站源码的要点。在本文中&#xff0c;我将为您深入探讨如何选择、优化和维护…

一个java项目中,如何使用sse协议,构造一个chatgpt的流式对话接口

前言 如何注册chatGPT&#xff0c;怎么和它交互&#xff0c;本文就不讲了&#xff1b;因为网上教程一大堆&#xff0c;而且你要使用的话&#xff0c;通常会再包一个算法服务&#xff0c;用来做一些数据训练和过滤处理之类的&#xff0c;业务服务基本不会直接与原生chatGPT交互。…

mysql-connector-java和spring-boot-starter-jdbc和mybatis-spring-boot-start

mysql-connector-java和spring-boot-starter-jdbc和mybatis-spring-boot-start JDBC是什么意思&#xff1f; JDBC是使用java语言操作mysql数据库的规范&#xff0c;java语言必须按照这个规范写才可以操作mysql数据库。 mysql-connector-java 在最开始的时候 程序中是不允许…

省级客运、货运量及周转量数据(1990-2022年)

1、数据介绍 客运量和货运量是衡量交通运输行业发展状况的重要指标&#xff0c;可以反映一个地区或国家的经济发展水平和人民生活水平。而周转量则是反映运输行业效率的指标&#xff0c;即货物或旅客被运输的总距离。 省级客运、货运量及周转量是衡量一个地区交通运输行业发展…

第⑮讲:Ceph集群管理与监控操作指南

文章目录 1.查看集群的状态信息2.动态的查看集群的状态信息3.查看集群的利用率4.查看OSD的资源利用率5.查看OSD的列表6.查看各组件的状态7.查看集群的仲裁信息8.查看/修改集群组件sock的配置参数 1.查看集群的状态信息 通过集群状态信息可以看到集群的健康状态、各个组件的运行…

uniapp app权限说明弹框2024.4.23更新

华为上架被拒绝 用uni-app开发的app&#xff0c;上架华为被拒&#xff0c;问题如下&#xff1a; 您的应用在运行时&#xff0c;未见向用户告知权限申请的目的&#xff0c;向用户索取&#xff08;电话、相机、存储&#xff09;等权限&#xff0c;不符合华为应用市场审核标准。…

Bingbong的回文路径

Here 利用回文串&#xff0c;从左往右与从右往左的hash值相同来判断从左往右&#xff0c;例&#xff1a;从右往左&#xff0c;例&#xff1a;由于在树上&#xff0c;考虑建两颗树&#xff0c;一颗根为最高位&#xff08;up&#xff09;&#xff0c;一棵根为最低位&#xff08;…

0 transformers入门,HuggingFace!

目录 1 了解 2 文本分类 1 了解 1 依赖安装 !pip install transformers -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 2 了解transformers 能做什么 from transformers.pipelines import SUPPORTED_TASKS SUPPORTED_TASKS.items()2 文本分类 我没外网所以…
最新文章