变换矩阵A和平移向量b的求解:
设相机坐标系绕X轴你是逆时针旋转 θ 后与云台坐标系的各个轴向平行,则旋转矩阵为
AngleSolver类结构
class AngleSolver : public RectPnPSolver
{
public:
AngleSolver(xxx);
void setScaleZ(double scale);
void setRelationPoseCameraPTZ(const cv::Mat & rot_camera_ptz, const cv::Mat & trans_camera_ptz, double y_offset_barrel_ptz);
void getTarget2dPoinstion(const cv::RotatedRect & rect, std::vector & target2d, const cv::Point2f & offset);
bool getAngle(const cv::RotatedRect & rect, double & angle_x, double & angle_y, double bullet_speed = 0, double current_ptz_angle = 0.0, const cv::Point2f & offset = cv::Point2f());
void tranformationCamera2PTZ(const cv::Mat & pos, cv::Mat & transed_pos);
void adjustPTZ2Barrel(const cv::Mat & pos_in_ptz, double & angle_x, double & angle_y, double bullet_speed = 0.0, double current_ptz_angle = 0.0);
public:
cv::Mat position_in_camera; //在相机坐标系下的坐标
cv::Mat position_in_ptz; //在云台坐标系下的坐标
private:
cv::Mat trans_camera2ptz; //相机坐标系到云台坐标系的平移向量
cv::Mat rot_camera2ptz; //相机坐标系到云台坐标系的旋转矩阵
// 炮管与云台在y轴上的偏移(cm)
double offset_y_barrel_ptz;
// 检测的距离(cm)
double min_distance;
double max_distance;
double scale_z;
};
//利用检测出来的旋转矩形计算出云台转角的函数
bool AngleSolver::getAngle(const cv::RotatedRect & rect, double & angle_x, double & angle_y, double bullet_speed, double current_ptz_angle, const cv::Point2f & offset)
{
if (rect.size.height < 1)
return false;
vector target2d;
getTarget2dPoinstion(rect, target2d, offset); //获取旋转矩形的顶点并排序
cv::Mat r;
//求解相机的外参,即从物体坐标系到相机坐标系的旋转矩阵rot、平移向量trans
solvePnP4Points(target2d, r, position_in_camera);
position_in_camera.at<double>(2, 0) = scale_z * position_in_camera.at<double>(2, 0);
if (position_in_camera.at<double>(2, 0) < min_distance || position_in_camera.at<double>(2, 0) > max_distance)
{
cout << "out of range: [" << min_distance << ", " << max_distance << "]\n";
return false;
}
//将目标在相机坐标系中的坐标转换成在云台坐标系中的坐标
tranformationCamera2PTZ(position_in_camera, position_in_ptz);
// 计算炮管的偏转角度
adjustPTZ2Barrel(position_in_ptz, angle_x, angle_y, bullet_speed, current_ptz_angle);
return true;
}
//求解相机的外参,即从物体坐标系到相机坐标系的旋转矩阵rot、平移向量trans
void RectPnPSolver::solvePnP4Points(const std::vector & points2d, cv::Mat & rot, cv::Mat & trans)
{
if(width_target < 10e-5 || height_target < 10e-5)
{
rot = cv::Mat::eye(3,3,CV_64FC1);
trans = cv::Mat::zeros(3,1,CV_64FC1);
return;
}
std::vector point3d;
double half_x = width_target / 2.0;
double half_y = height_target / 2.0;
point3d.push_back(Point3f(-half_x, -half_y, 0));
point3d.push_back(Point3f(half_x, -half_y, 0));
point3d.push_back(Point3f(half_x, half_y, 0));
point3d.push_back(Point3f(-half_x, half_y, 0));
cv::Mat r;
cv::solvePnP(point3d, points2d, cam_matrix, distortion_coeff, r, trans);
Rodrigues(r, rot);
}
//像素坐标转换到摄像机的坐标
void RectPnPSolver::solvePnP4Points(const std::vector & points2d, cv::Mat & rot, cv::Mat & trans)
{
if(width_target < 10e-5 || height_target < 10e-5)
{
rot = cv::Mat::eye(3,3,CV_64FC1);
trans = cv::Mat::zeros(3,1,CV_64FC1);
return;
}
std::vector point3d;
double half_x = width_target / 2.0;
double half_y = height_target / 2.0;
point3d.push_back(Point3f(-half_x, -half_y, 0));
point3d.push_back(Point3f(half_x, -half_y, 0));
point3d.push_back(Point3f(half_x, half_y, 0));
point3d.push_back(Point3f(-half_x, half_y, 0));
cv::Mat r;
//将图像坐标转换到摄像机坐标
cv::solvePnP(point3d, points2d, cam_matrix, distortion_coeff, r, trans);
Rodrigues(r, rot);
}
//opencv函数solvePnP
bool cv::solvePnP ( InputArray objectPoints,
InputArray imagePoints,
InputArray cameraMatrix,
InputArray distCoeffs,
OutputArray rvec,
OutputArray tvec,
bool useExtrinsicGuess = false,
int flags = SOLVEPNP_ITERATIVE
)
//获取旋转矩形的顶点并排序
void AngleSolver::getTarget2dPoinstion(const cv::RotatedRect & rect, vector & target2d, const cv::Point2f & offset){
Point2f vertices[4];
rect.points(vertices); //获取旋转矩形的四个定点
Point2f lu, ld, ru, rd;
sort(vertices, vertices + 4, [](const Point2f & p1, const Point2f & p2) { return p1.x < p2.x; }); //按照定点的x坐标进行排序
if (vertices[0].y < vertices[1].y){
lu = vertices[0];
ld = vertices[1];
}
else{
lu = vertices[1];
ld = vertices[0];
}
if (vertices[2].y < vertices[3].y) {
ru = vertices[2];
rd = vertices[3];
}
else {
ru = vertices[3];
rd = vertices[2];
}
target2d.clear();
target2d.push_back(lu + offset);
target2d.push_back(ru + offset);
target2d.push_back(rd + offset);
target2d.push_back(ld + offset);
}
//角度工厂中getAngle函数,判断角度求解器是否为空,并为角度求解器设置参数
bool AngleSolverFactory::getAngle(const cv::RotatedRect & rect, TargetType type, double & angle_x, double & angle_y, double bullet_speed, double current_ptz_angle, const cv::Point2f & offset)
{
if(slover == NULL){
std::cerr << "slover not set\n";
return false;
}
double width = 0.0, height = 0.0;
if(type == TARGET_RUNE){
width = rune_width;
height = rune_height;
}
else if(type == TARGET_ARMOR){
width = armor_width;
height = armor_height;
}
else if(type == TARGET_SAMLL_ATMOR){
width = small_armor_width;
height = small_armor_height;
}
cv::RotatedRect rect_rectifid = rect;
AngleSolverFactory::adjustRect2FixedRatio(rect_rectifid, width/height);
slover->setTargetSize(width, height);
return slover->getAngle(rect_rectifid, angle_x, angle_y, bullet_speed, current_ptz_angle, offset);
}