问题
I am working on a solution for calibrating a laser projector in the real world. There are a few goals for this project.
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.
Example:
A rectangular flat table has a target on each corner. One of the corners the target is raised in the air by a shim by 50mm (~2 inches). Just because the target is raised 50mm, I do not want my projection surface to be skewed. I want to account for the height and still project the final product on a flat Z = 0 plane. The coordinates for these targets in real world space are shown below (values are all in millimeters):
The laser coordinates range from -32000 to 32000, they use photosensors to capture the targets in the real world and when captured return values in the laser coordinate system. The Coordinates captured are shown below. (values range from -32000 to 32000 and their units are unknown to me).
Goal:
Determine if the laser coordinates captured correctly represent the real world coordinates provided (if not, determine a deviation from those coordinates) and then provide a transformation matrix or some other method of transforming points in 3d real world space accurately to 2d laser space.
What I have tried:
I have tried implementing a few different perspective correction solutions. One from OpenCvSharp3, and one from a code project thread. Both of these work equally well but both have problems.
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.
If anyone has attempted/solved/come across this problem before and could shed any insight it would be greatly appreciated.
Thank you in advance!
回答1:
Here a sample code that show the proper calculation required. Imagine you real table X,Y is X horizontal with the picture and Y going up. The origin being 0,0,0 for convenience and easier sample to follow. Then the second image imagine the proper X,Y to be the top left corner where X axis is going down and Y going right (-90 degree rotation ish). Don't worry about that it's easily adjustable later. The comments in the quick sample below explain what every line does and why it's there.
// 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);
So here you have it. The sample center point on the real table translated to {12203,5947,0}
using this code which is pretty much dead center in the laser matrix.
Now for the skew part. This is also pretty simple. this line :
laserMatrix.Rotate(new Quaternion(new Vector3D(0, 0, 1), -90d));
It's all you need to change. What you want is always consider that top left corner is the origin so all you want to do is imagine a straight line going toward the right (on you laser image) and you want to figure out what is the angle between this perfect X axis (vector(1,0,0)) and the vector of the bottom left point which in this case is slightly more than 90 degree (probably between -91 to -95 degree). You want to figure out the angle in Z to achieve it. It doesn't matter if the formula you use return +270 degree instead of -90 degree since it's a matrix it will give the same results.
Here a method that comput angle in degree between 2 vector based of a rotation axis given
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;
}
a sample usable is :
var angle = AngleToInDegree(new Vector3D(1,0,0), new Vector3D(0,1,0), new Vector3D(0,0,1));
This checks the angle between a perfect X vector and perfect Y vector relative to a perfect Z vector which will give you 90 degree.
来源:https://stackoverflow.com/questions/52615168/laser-projector-calibration-in-3d-space