【问题标题】:OpenCV Kalman Filter ErrorOpenCV 卡尔曼滤波器错误
【发布时间】:2012-05-14 14:32:30
【问题描述】:

我使用 OpenCV 来预测通过网络摄像头看到的球的运动。但是我不断收到关于 cvKalmanPredict 状态的错误,因此我将代码简化为这几行并尝试单独测试过滤器:

        CvKalman* kalman = cvCreateKalman(6,3,1);
        kalman->temp2 = cvCreateMat(1, 1, CV_32FC1);

        float alpha = 0.1, beta = 0.2;

        float kalmA[] = {1.0+t0/t1, 0, 0, -t0/t1, 0, 0,
                         0, 1.0+t0/t1, 0, 0, -t0/t1, 0,
                         0, 0, 1.0+t0/t1, 0, 0, -t0/t1,
                         1, 0, 0, 0, 0, 0,
                         0, 1, 0, 0, 0, 0,
                         0, 0, 1, 0, 0, 0};
        float kalmB[] = {0, 0, 1, 0, 0, 0};
        float kalmH[] = {1, 0, 0, 0, 0, 0,
                         0, 1, 0, 0, 0, 0,
                         0, 0, 1, 0, 0, 0};
        float kalmQ[] = {alpha, 0, 0, 0, 0, 0,
                         0, alpha, 0, 0, 0, 0,
                         0, 0, beta, 0, 0, 0,
                         0, 0, 0, alpha, 0, 0,
                         0, 0, 0, 0, alpha, 0,
                         0, 0, 0, 0, 0, beta};
        float kalmR[] = {alpha, 0, 0,
                         0, alpha, 0,
                         0, 0, beta};
        float kalmS[] = {0,0,0, 0, 0, 0};
        float kalmP[] = {480, 0, 0, 0, 0, 0,
                         0, 480, 0, 0, 0, 0,
                         0, 0, 480, 0, 0, 0,
                         0, 0, 0, 480, 0, 0,
                         0, 0, 0, 0, 480, 0,
                         0, 0, 0, 0, 0, 480};

        memcpy( kalman->transition_matrix->data.fl, kalmA, sizeof(kalmA) );
        memcpy( kalman->control_matrix->data.fl, kalmB, sizeof(kalmB) );
        memcpy( kalman->measurement_matrix->data.fl, kalmH, sizeof(kalmH) );
        memcpy( kalman->process_noise_cov->data.fl, kalmQ, sizeof(kalmQ) );
        memcpy( kalman->measurement_noise_cov->data.fl, kalmR, sizeof(kalmR) );


        // initialize state and covariance
        memcpy( kalman->state_post->data.fl, kalmS, sizeof(kalmS) );
        cvSetIdentity( kalman->error_cov_post, cvRealScalar(3));

        // update the control
        float t0 = 0.3;
        cvSetReal2D( kalman->temp2, 0, 0, -490 * t0 * t0 );

        const CvMat* kalmanPred = cvKalmanPredict(kalman, kalman->temp2);

        CvMat* kalmMeas = cvCreateMat(3,1,CV_32FC1);
        cvSetReal2D(kalmMeas, 0, 0, 3);
        cvSetReal2D(kalmMeas, 1, 0, 2);
        cvSetReal2D(kalmMeas, 2, 0, 5.5);
        cvKalmanCorrect(kalman, kalmMeas);
        cvReleaseMat(&kalmMeas);

        // release memory

但是,我在调用 cvKalmanPredict 时仍然遇到同样的错误:

        OpenCV Error: Assertion failed ((D.rows == ((flags & CV_GEMM_A_T) == 0 ? A.rows : A.cols)) && (D.cols == ((flags & CV_GEMM_B_T) == 0 ? B.cols : B.rows)) && D.type() == A.type() in unknown function. file C:\Users\opencv\modules\core\src\matmul.cpp. line 2930

我正在使用带有 MS Visual C++ 10 的 cmake 进行编译。

【问题讨论】:

  • 您的尺寸或类型似乎有问题。将矩阵复制到 kalman->transition_matrix->data 和其他人之后,您是否检查过它们的样子?
  • 我在调用 kalmanPredic 之前为所有矩阵打印 data.fl 中的内容。对我来说一切正常,这就是为什么我不知道这个错误来自哪里。

标签: c++ opencv kalman-filter


【解决方案1】:

似是而非的解释

在阅读详细的错误消息时,断言似乎是在测试 A 和 B 是否符合 D 的大小。大概此检查发生在涉及这三个矩阵的计算 (cvKalmanPredict) 之前。

错误消息中指定的矩阵 A、B 和 D 可能与示例中提供的三个或更多矩阵直接相关(A 可能对应于kalmA 等)。

需要更多代码来阐明 A、B 和 D 与声明的矩阵之间的关系(如果有)。在 openCV 库中设置断点可能会有所帮助。

列出矩阵大小可能会提供一个见解:

Variable  Size   Variable  Size   Variable  Size 
kalmA     6x6    kalmQ     6x6    kalmR     3x3
kalmB     6x6    kalmS     6x6         
kalmH     6x6    kalmP     6x6     

一个。矩阵大小

由于kalmR 是唯一大小不同的矩阵,因此该矩阵可能是断言失败的根源。

如果断言只涉及其他 6x6 矩阵的任何三元组合,则不会触发断言,但会触发。这表明矩阵变量kalmR 是开始跟踪代码的有用变量。

此逻辑假定 A、B 和 D 映射到表中列出的三个矩阵。

b.转置矩阵

数组的大小可能正确,但可能被错误地转置。

上表假定矩阵以 6x6 格式解释,而不是解释为 1x36 向量或其转置、36x1 列格式。它们在编译器提示的表达式flags & CV_GEMM_B_T 中的关联标志可以设置为指示数组是MxN 还是NxM 格式。

或者,可以重写违规矩阵的初始化值以符合 openCV 的 GEMM(Generalized Matrix Multiplication) 函数所期望的格式。该操作被描述为泛化,因为它包含的不仅仅是两个矩阵之间的乘法。

c。复合错误

错误消息可能是两点的某种组合 a。和 b。


另类解释

另一种解释是断言编码不正确。错误消息中 B 矩阵的存在似乎与断言的意图不一致。

//should that solitary B be an A?
D.rows == .. A.rows && D.cols == .. B.cols && D.type() == A.type()

逻辑and中的最后一个操作数是测试两个矩阵D和A的类型。

D.type() == A.type()

在类型比较之前,是一对操作数,分别比较D和A的行数,以及D和A的列数。

//simplified assertion with B replaced by A
D.rows == .. A.rows && D.cols == .. A.cols  

断言似乎是在明确检查两个矩阵的类型之前检查两个矩阵是否具有相同的维度。

任何两个具有不同行数或列数的矩阵都会在断言中排除对type() 的调用,因为&& 会短路。然而在最初的断言中,只有一个维度的 D 和 A 被测试是否相等,这表明检查是确定两个矩阵是否兼容乘法。

同样的逻辑也适用于将 A 替换为 B 的情况:

D.rows == .. B.rows && D.cols == .. B.cols && D.type == B.type()


矩阵运算之前的断言检查

令人惊讶的是,从编译器发出的错综复杂的错误消息中,我们可以猜测与断言相关的矩阵运算——这可以在没有访问 openCV 库的源代码并且没有任何 openCV 文档的情况下实现。

在逻辑 and 操作数中有足够的信息来部分恢复与 openCV GEMM 函数中编程的矩阵方程一致的矩阵方程(涉及 A、B 和 D)(触发错误消息)。

这与在文件中查找特定函数和行号不同。区别在于我们根据断言中测试的一系列数学属性来推断矩阵运算的序列。

逻辑如下:

一个。初步猜测

断言的重点在于变量 D,因为它出现在每个 操作数中。

该断言暗示 D 必须以某种方式与 A 和 B 兼容,而它没有提及 A 与 B 的适用性。

也许这对矩阵之前进行过比较,发现在某种意义上是兼容的。(在错误消息中,与 A 和 B 相关的位掩码 CV_GEMM_A_TCV_GEMM_B_T 的存在支持此视图 - 信息A 和 B 与 D 不同,包含在奇异变量 flags 中。

这也暗示了为什么断言中没有类型比较D.type() == B.type(),因为它是多余的。

考虑到这一点,并用重新排序的操作数来重新编写断言以收集相似的术语,我们有:

D.rows == .. A.cols && D.type() == A.type() && D.cols == .. B.rows

D 的行大小与 A 的列大小的比较表明 D * A,一个乘法运算(加法运算需要比较每个矩阵维度,这在此处没有发生)。

对于 D 和 B 也是如此——在这种情况下,将根据 B 的行大小检查 D 的列大小——这个测试与乘法运算一致,所以我们有 B * D

将这些术语收集在一起,建议D * A + B * D 或可能D * A - B * D 或一些类似的矩阵组合由 GEMM 函数执行。

b.第二次猜测

然后继续作为乘法或加法操作数,D 被重新发明以存储 A 和 B 之间的某些矩阵运算序列的结果。最简单的运算序列是 A * BA + B

断言中的行和列检查支持前一种猜测,D = A * B

成对变量 A、B 足够相关,可以折扣其他组合,例如 B = D * AA = B * D

D 作为结果的目的地的角色,暂时由指定给该变量的字母表示。

c。是否有任何猜测恢复了矩阵运算?

在阅读 openCV 文档时,第二个猜测被确定为最接近正确的描述。

特别是,触发断言的 openCV 函数可以是三个例程之一:cvMatMulcvMatMulAddcvGEMM,其对应的矩阵运算是:

D =   A * B         // cvMatMul
D =   A * B +   C   // cvMatMulAdd 
D = α A * B + β C   // cvGEMM - Generalized Matrix Multiplication 
                    //          alpha, beta are scalars unlike A, B, C, D 

有关这些函数声明的更多信息记录在 here

A 和 B 的按位 flags 变量

在上面,我们假设flags变量中的位字段由CV_GEMM_A_T和CV_GEMM_B_T标识。类似的讨论适用于这些标志设置的各种组合。

CV_GEMM_A_TCV_GEMM_B_T对应的位不为零时,两个断言中的第一个适用(撇号表示转置操作):

// matrix-size selected by the conditional operator when the ...
D.rows == .. A'.rows && D.cols == .. B'.cols    // ... bit fields are set 
D.rows == .. A .cols && D.cols == .. B .rows    // ... bit fields are unset

为了比较,当子表达式flags & CV_GEMM_A_Tflags & CV_GEMM_B_T 为零时,断言采用不同的形式。

错误消息暗示 flags 变量至少表示矩阵 A 和 B。openCV 文档指定所有三个输入矩阵 A、B 和 C 都可以由这个单一变量表示。

最后一点:在矩阵运算之前进行断言检查

在抢占断言所暗示的特定矩阵计算时,我们正在缩小通过代码的可能路径和矩阵算法所在的位置。

为了促进这种合理的猜测,矩阵计算的断言应该以一种形式编写,以暗示它们所保护的底层矩阵运算。

这里我们研究了简单的矩阵表达式。当遇到由不太简单的矩阵算法生成的类似错误消息时,所描述的方法可能对破译在触发断言中编码的加密矩阵运算很有用。

扩展:自动生成断言语句

可能有一种方法可以编写矩阵代码来自动生成此类断言,也许可以使用模板。可以对矩阵类和矩阵运算进行编码以促进此类检查的生成。

【讨论】:

  • 非常感谢您的建议。问题是我使用卡尔曼滤波器中的一个临时变量并将其初始化为 3 x 1 矩阵。在 cvKalmanPredict 中,他们尝试存储两个 6 x 6 矩阵的乘积,这引入了错误。
猜你喜欢
  • 2011-04-14
  • 2017-08-11
  • 2019-03-30
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多