我正在研究在现实世界中校准激光投影仪的解决方案。这个项目有一些目标。
1. Take in a minimum of four points measured in the real world in 3d space that represent the projection surface.
2. Take in coordinates from the laser that are equivalent to the points received in part 1
3. Determine if the calibration file matches the real world captured points and show a deviation between the coordinate spaces
4. Using the data from the previous steps, take coordinates in 3d real world space and translate them to laser coordinates.
示例:
一个矩形平板在每个角上都有一个目标。目标物在空中的一角由垫片抬高50mm(〜2英寸)。仅仅因为目标被提升了50mm,我才不希望我的投影表面倾斜。我想考虑高度,但仍将最终产品投影在平面Z = 0的平面上。现实世界中这些目标的坐标如下所示(值均以毫米为单位):
激光坐标的范围是-32000到32000,他们使用光电传感器捕获现实世界中的目标以及在激光坐标系中捕获返回值时。捕获的坐标如下所示。 (值的范围是-32000到32000,我不知道它们的单位)。
目标:
确定捕获的激光坐标是否正确代表提供的真实世界坐标(如果不是,请确定与这些坐标的偏差),然后提供转换矩阵或将3d真实世界空间中的点准确转换为2d激光空间的其他方法
我尝试过的事情:
我尝试实施一些不同的透视校正解决方案。一个来自OpenCvSharp3,另一个来自代码项目线程。两者均能很好地工作,但都有问题。
1. I cannot determine a deviation from the real world. The points are perfectly mapped to the perspective representation so I cannot determine if they are misaligned from the real world expectation
2. I cannot represent source points in a 3d space. Targets may be placed arbitrarily in the Z-direction, and with perspective mapping I cannot seem to account for that Z Direction.
如果有人以前曾尝试/解决/遇到过此问题,并且有可能提出任何见解,将不胜感激。
提前谢谢!
答案 0 :(得分:-1)
以下示例代码显示所需的正确计算。想象一下,您的真实桌子X,Y是X水平的,图片和Y都在上升。为方便起见,样本的原点是0,0,0。然后,第二个图像将适当的X,Y想象为X轴向下并且Y右侧向右旋转(-90度旋转)的左上角。不用担心以后很容易进行调整。下面的快速示例中的注释说明了每一行的功能以及为何存在该行。
// compute from the points the table height and width
var realTableDimX = 902d;
var realTableDimY = 597d;
// the real table matrix is 0,0,0 on bottom corner
// so matrix is identity (our source)
var realTableMatrix = new Matrix3D();
// the laser is rotated 90 degree and position at the top left based compared to the realTablematrix
var laserMatrix = new Matrix3D();
// rotate and translate the laser matrix into position
// 90 degree doesnt account for the little skew (see comment outside code for explaination on how to fix that)
laserMatrix.Rotate(new Quaternion(new Vector3D(0, 0, 1), -90d));
laserMatrix.Translate(new Vector3D(0, realTableDimY, 0d));
// compute the laser dimensions (i used your points you found out)
var laserDimX = 20392d - (-16300d);
var laserDimY = 12746 - (-11409d);
// calculate the ratio to factor in to displace the point to the real value onto the laser matrix
var laserXRatio = laserDimX / realTableDimX;
var laserYRatio = laserDimY / realTableDimY;
// since matrix are 1 to 1 when you compute if you have an equal ratio of 40 in X and 40 in Y then the point at 10,10,0 on
// the real table is correcponding to the point 400,400,0 on the laser matrix. But since it's rotated 90 degree you
// will need to apply the Y ration to the X point after the transform and the X ratio to the Y point to get the actual point.
// sample point on table. Logic can be flipped with laser matrix to do it the other way
var sampleRealTablePoint = new Point3D(450, 300, 0); // roughly centered
// transform the point from one UCS to another is simply multiplying by it's current UCS (matrix it's in)
// to transform it to world UCS then multiply by the inverse of other UCS we want it in (the other matrix)
var sampleRealTablePointWorld = realTableMatrix.Transform(sampleRealTablePoint);
// convert that point into the laser matrix but first create and inverted matrix of the laser matrix
// we copy a matrix to not modify the current one when inverting it
var laserInvertedMatrix = new Matrix3D() * laserMatrix;
laserInvertedMatrix.Invert();
// get the sample point in the world of the laser matrix
var sampleRealTablePointToLaserMatrix = laserInvertedMatrix.Transform(sampleRealTablePointWorld);
// not adjust the X and Y like said earlier
var finalAdjustedPoint = new Point3D(sampleRealTablePointToLaserMatrix.X * laserXRatio, sampleRealTablePointToLaserMatrix.Y * laserYRatio, 0d);
// this is if you want the point in the world of the laser matrix and not it's offset from the 0,0,0
// the vector here is the top left corner of your laser matrix (x, y, z)
var laserWorldFinalPoint = finalAdjustedPoint + new Vector3D(-11409d, -16155d, 0d);
所以您在这里。使用此代码将真实表上的样本中心点转换为{12203,5947,0}
,该代码在激光矩阵中几乎是死点。
现在是歪斜部分。这也很简单。这行:
laserMatrix.Rotate(new Quaternion(new Vector3D(0, 0, 1), -90d));
这就是您需要更改的所有内容。您一直想要的是始终认为左上角是原点,因此您所要做的就是想象一条向右的直线(在您的激光图像上),并且您想弄清楚此完美X轴之间的角度是多少(向量(1,0,0))和左下角点的向量,在这种情况下,该点略大于90度(可能在-91到-95度之间)。您想弄清楚Z的角度来实现它。您使用的公式是否返回+270度而不是-90度并不重要,因为它是一个矩阵,将给出相同的结果。
这是一种根据给定的旋转轴计算两个矢量之间的角度的方法
public double AngleToInDegree(Vector3D v, Vector3D vector, Vector3D normal)
{
var dotNormal = Vector3D.DotProduct(normal, Vector3D.CrossProduct(v, vector));
var dotVector = Vector3D.DotProduct(v, vector);
var angle = Math.Atan2(dotNormal, dotVector);
return angle * 180d / Math.PI;
}
可用的示例是:
var angle = AngleToInDegree(new Vector3D(1,0,0), new Vector3D(0,1,0), new Vector3D(0,0,1));
这将检查完美X向量和完美Y向量相对于完美Z向量之间的夹角,这将为您提供90度。