如何使用PID控制器为A.R Drone 2.0?

时间:2017-11-04 15:26:55

标签: c++ algorithm opencv image-processing ar.drone

我一直在查看自动无人机的代码,并在此存储库中遇到了这个代码:https://github.com/puku0x/cvdrone。我试图理解代码,我是控制器算法和OpenCV的新手。我试过去OpenCV网站并了解功能,但它没有多大帮助。任何帮助将不胜感激。

        // Tracking

    if (contour_index >= 0) {
        // Moments
        cv::Moments moments = cv::moments(contours[contour_index], true);
        double marker_y = (int)(moments.m01 / moments.m00);
        double marker_x = (int)(moments.m10 / moments.m00);

        // Show result
        cv::Rect rect = cv::boundingRect(contours[contour_index]);
        cv::rectangle(image, rect, cv::Scalar(0, 255, 0));

        if (track) {
            const double kp = 0.005;
            vx = kp * (binalized.rows / 2 - marker_y);;
            vy = 0.0;
            vz = kp; 
            vr = kp * (binalized.cols / 2 - marker_x);
            std::cout << "(vx, vy, vz, vr)" << "(" << vx << "," << vy << "," << vz << "," << vr << ")" << std::endl;
            std::cout << "Altitude = " << ardrone.getAltitude() << "%" << std::endl;
        }
              // Marker tracking
    if (track) {
        // PID gains
        const double kp = 0.001;
        const double ki = 0.000;
        const double kd = 0.000;

        // Errors
        double error_x = (binalized.rows / 2 - marker.y);   // Error front/back
        double error_y = (binalized.cols / 2 - marker.x);   // Error left/right

        // Time [s]
        static int64 last_t = 0.0;
        double dt = (cv::getTickCount() - last_t) / cv::getTickFrequency();
        last_t = cv::getTickCount();

        // Integral terms
        static double integral_x = 0.0, integral_y = 0.0;
        if (dt > 0.1) {
            // Reset
            integral_x = 0.0;
            integral_y = 0.0;
        }
        integral_x += error_x * dt;
        integral_y += error_y * dt;

        // Derivative terms
        static double previous_error_x = 0.0, previous_error_y = 0.0;
        if (dt > 0.1) {
            // Reset
            previous_error_x = 0.0;
            previous_error_y = 0.0;
        }
        double derivative_x = (error_x - previous_error_x) / dt;
        double derivative_y = (error_y - previous_error_y) / dt;
        previous_error_x = error_x;
        previous_error_y = error_y;

        // Command velocities
        vx = kp * error_x + ki * integral_x + kd * derivative_x;
        vy = 0.0;//kp * error_y + ki * integral_y + kd * derivative_y;
        vz = 0.0;
        vr = 0.0;

    }
    }

    // Display the image
    cv::putText(image, (track) ? "track on" : "track off", cv::Point(10, 20), cv::FONT_HERSHEY_SIMPLEX, 0.5, (track) ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0), 1, cv::LINE_AA);
    cv::imshow("camera", image);
    ardrone.move3D(vx, vy, vz, vr);
}

1 个答案:

答案 0 :(得分:2)

您的问题有点笼统,但我会看看能否为您提供一个帮助您入门的概述。 (另请参阅此类似问题:Tracking objects from camera; PID controlling; Parrot AR Drone 2。)

此代码(来自cvdrone存储库中的sample_tracking.cpp)正在尝试执行以下操作:

  1. 从相机中查找最大的物体(所需颜色)。
  2. 移动无人机,使对象位于摄像机视图的中心。
  3. 它使用OpenCV来完成第一个任务,使用PID来完成第二个任务。

    从相机中查找最大的物体(所需颜色)

    为此,代码从无人机的视频摄像头抓取一个帧,寻找所需颜色的最大斑点,并找到该斑点的中心。

    它使用OpenCV通过执行以下操作来实现此目的:

    1. 使用InRange阈值图像,这会打开接近目标颜色的像素,并关闭远处的像素。现在你有一个只包含白色和黑色像素的图像,其中白色像素对应于你正在寻找的颜色。这篇博文有一个很好的例子,可以使用InRange在魔方的立方体上找到某种颜色的立方体:Color spaces in OpenCV

    2. morphologyExMORPH_CLOSE一起使用可消除图像中的噪点。这应该可以更容易地找到所需的blob。见第4节,&#34;结束&#34;在this page上查看此处理结果的示例。

    3. 使用findContours查找图像中的像素斑点。轮廓是连接所有连续点(沿着边界)的曲线,具有相同的颜色或强度&#34;,因此这将找到图像中所有白色斑点的轮廓。

    4. 示例输入:

      enter image description here

      示例结果:

      enter image description here

      1. 使用contourArea按区域查找最大轮廓。这将是无人机将追踪的目标。

      2. 使用moments查找最大轮廓的质心。这是它如何确定对象的图像坐标。 This page documenting contour features提供了有关时刻的更多信息。

      3. 所以现在代码具有要跟踪的对象的坐标。接下来是实际移动无人机进行跟踪的部分。

        移动无人机,使对象位于摄像机视图的中心

        PID控制是一个很大的主题。我只是描述基础知识和这段代码的作用;如果您需要更多信息,可以使用大量介绍性资源,例如本文"Introduction to PID control"或此视频:"PID Control - A brief introduction"

        PID控制器考虑了3件事:

        1. 比例期限:我们离我们想要的地方有多远?
        2. 衍生术语:我们走向(或远离)我们想要的位置有多快?
        3. 积分项:我们所处的位置和想要的位置之间的累积误差是多少? (积分术语)
        4. 比例术语将您带向目标。当您快速向目标移动时,衍生术语会减慢您的速度,因此您不会超调。当你离目标稍远一点时,积分术语可以帮助推动你。

          事实证明,由于以下3行代码,此函数并未真正运行完整的PID控制器:

          // PID gains
          const double kp = 0.001;
          const double ki = 0.000;
          const double kd = 0.000;
          

          由于控制器的积分和微分部分的增益为0,这只是一个简单的比例控制器:代码只是查看目标坐标和图像中心之间的差异,并使用它决定如何驾驶无人机。

          首先,这是cvdrone代码uses for the AR.Drone coordinate system

          的内容
            

          AR.Drone的前面是X轴,左边是Y轴,上面是Z轴。也   前面是0.0 [rad],每个轴CCW是正的。

                  X
                 +^-
                  |
                  |
          Y <-----+ (0,0)
                  Z
          

          在这里计算无人机运动然后命令:

          // Command velocities
          vx = kp * error_x + ki * integral_x + kd * derivative_x;
          vy = 0.0;//kp * error_y + ki * integral_y + kd * derivative_y;
          vz = 0.0;
          vr = 0.0;
          
          // ...
          
          ardrone.move3D(vx, vy, vz, vr);
          

          因此,代码计算vx,这是无人机所需的前向/后向速度,为kp * error_x(其他项为零,因为kd和{{1}是零)。 ki是图像中目标的Y坐标与图像的中心Y坐标之间的像素增量。

          我不记得AR.Drone相机的实际分辨率,但我们假设它是320x480。这意味着图像的中心位于y = 240。

          如果目标的中心靠近图像的顶部,比如说y = 15像素,那么error_x。然后是error_x = 240 - 15 = 225。调用vx = kp * error_x = 0.001 * 225 = 0.225会导致无人机以0.225的速度向前移动。

          假设在下一个时间步,无人机向前移动了一点,结果静止跟踪物体在相机的视野中略微向下移动,所以现在中心位于y = 40像素。然后是move3Derror_x = 240 - 40 = 200。所以现在调用vx = 0.001 * 200 = 0.200导致无人机继续向前移动,但比之前更慢。

          当无人机向前移动并且目标移近摄像机视野的中心时,无人机将继续减速。很可能无人机将超过目标,并且希望相机会看到目标略低于视野中心,这将导致负move3D和负error_x,导致无人机慢慢向后移动。

          对于此代码如何使用OpenCV和PID控制器来跟踪对象,这非常快速。通常,您实际上每个轴都有一个单独的PID控制器,您可以将无人机移动到:前进/后向平移,左/右平移,顺时针/逆时针旋转。您可以尝试将vxkd项分别设置为小的负值和小值,以查看它如何改变无人机的行为。 This video是我制作的,演示了使用AR.Drone和两个PID控制器,一个用于前进/后退,一个用于左/右,用于跟踪一系列航路点。

          enter image description here

          还有其他方法可以使用OpenCV进行对象跟踪。 This video演示了使用camshift / meanshift技术(不是在无人机上)。 This video演示了如何在AR.Drone上使用颜色直方图跟踪。

          enter image description here