Opencv:如何用坐标计算物体的度数(角度)

Opencv: how to calculate the degrees(angles) of an object with its coordinates

我目前正在从事一个项目,该项目将运动检测与玩具枪在 (x,y) 轴上的移动相结合。我设法获得了物体位置的坐标(使用 OpenCV 正确的命令)并将它们发送到 Arduino 以移动枪支,但这没有正常工作。我使用的代码是这样的:

#include <Servo.h>

#define servomaxx 114//max degree servo horizontal (x) can turn
#define servomaxy 120//max degree servo vertical (y) can turn
#define screenmaxx 480 //max screen horizontal (x) resolution
#define screenmaxy 320 //max screen vertical (y) resolution
#define servocenterx 90 //center position on (x)
#define servocentery 90 //center position on (y)
#define servopinx 9 //digital pin for servo x
#define servopiny 10 //digital pin for servo y
#define baudrate 9600 //com port speed. 
#define servorange 180

int x100byte;
int x010byte;
int x001byte;

int posx_taken=0;
int posy_taken=0;
int posx = 0;
int posy = 0;

Servo servox;
Servo servoy;


void setup()
{
    Serial.begin(9600);     
    pinMode(servopinx,OUTPUT);    
    pinMode(servopiny,OUTPUT);    
    pinMode(13, OUTPUT);

    servoy.attach(servopiny);
    servox.attach(servopinx);

    servox.write(servocenterx);
    delay(200);
    servoy.write(servocentery);
    delay(200);
}

void loop()
{
    if (Serial.available() > 3) {
        x100byte = Serial.parseInt();
        delay(5);
        x010byte = Serial.parseInt();
        x001byte = Serial.parseInt();

        Serial.println("x100byte");
        Serial.println(x100byte);
        Serial.println("x010byte");
        Serial.println(x010byte);

        posx_taken=x100byte;
        posy_taken=x010byte;

        if ((posx_taken>screenmaxx) || (posy_taken>screenmaxy)) {
            Serial.println("Wrong number-too big values!");
        } else {
            if (posx_taken == screenmaxx) {
                posx = servomaxx;
            } else if (posx_taken == 0) {
                posx = 68;
            } else if (posx_taken == (screenmaxx / 2)) {
                posx=servorange / 2;
            } else {
                posx = ((posx_taken * 24) / (screenmaxx / 2)) + 66;
            }

            if (posy_taken == screenmaxy) {
              posy = 120;
            } else if (posy_taken == 0) {
              posy = 60;
            } else if (posy_taken == (screenmaxy / 2)){
              posy = 90;
            } else {
              posy = ((posy_taken * 30) / (screenmaxy / 2)) + 60;
            }
        }

        servox.write(posx);
        servoy.write(posy);  

        if (x001byte == 180) {
            digitalWrite(13, HIGH);  
            delay(1000);             
            digitalWrite(13, LOW); 
        }
    }
}

我根据水平和垂直相机角度(45o V,58o H)计算了每个舵机的垂直和水平运动的最小和最大度数。在这种情况下,水平舵机的 (x) 最小值为 66 度,最大值为 112 度。此外,垂直舵机的 (y) 最小值为 60 度,最大值为 120 度。

此时我想问一下有没有一个公式可以根据相机的角度将物体的坐标转换为度数。上面的代码没有在正确的位置转动枪。谁能帮帮我?

我同时使用 ASUS Xtion Pro Live 摄像头(45o V、58o H、70o D)和 USB 摄像头。

您可以使用一些简单的三角函数来解决这个问题。

首先,让我们假设已消除任何由光学引起的失真,并且图像中心与光学中心一致。我们可以独立处理每个平面(水平和垂直)以简化问题。

我们可以将图像视为透视投影。从上面看(即仅考虑水平面),它看起来像这样:

图像的宽度已知(根据您的代码为 480 像素),以及角度 α(您所说的水平 FOV 的一半为 58°)。

因此:

a = w / 2 = 480 / 2 = 240 pixels

α = 58° / 2 = 29°

根据三角学,我们有以下等式:

tan(α) = a / d

tan(β) = b / d

相当于

d = a / tan(α)

d = b / tan(β)

d当成常见的,我们就可以写成

a / tan(α) = b / tan(β)

相当于

a * tan(β) = b * tan(α)

给我们

tan(β) = b * tan(α) / a

最后

β = arctan(b * tan(α) / a)


现在我们可以将我们已知的常数代入这个等式:

β = arctan(b * tan(29°) / 240)

由于角度 α 是常数,因此它的切线也是常数。表达为

可能更方便

K = tan(29°) / 240

β = arctan(b * K)


现在,您可能不想在 AVR 上进行实际计算。一种更灵活的方法可能是使用查找 table(每个轴一个),它将像素位置映射到角度(或者甚至更好的伺服目标值)。

您可以将它作为程序内存中的静态数组,或者如果您有它的 space,请将其保存在 SRAM 中,并允许通过串行端口上传新的 LUT。