📢前言
这里假定已经完成标定,计算出了上视相机和下视相机的像素当量,不了解贴装流程和贴装涉及到的标定可以参考我的博文:
贴装流程是下视相机拍照固定位置B,计算出其中心坐标和定位相机中心的偏差,以及角度。然后吸嘴吸取产品A移至上视相机上方第一次拍照,旋转吸嘴校正计算出的产品A与固定位置B处的角度偏差,然后进行第二次拍照验证角度校正是否到位,若不到位,继续旋转吸嘴校正角度。若到位,则计算出产品A与吸嘴之间的偏差,通过前面已知的固定位置B的中心坐标,移动吸嘴,使产品A贴合至固定位置B处。下面说一下在这个贴装过程中需要用到那些数据信息以及如何进行计算:
🚄贴装动作流程以及涉及到的位置计算
- 下视相机拍照固定位置B时,求出固定位置B中心和下视相机中心的偏差->在通过固定位置B与吸嘴之间的间距,计算处固定位置B的中心物理坐标和角度。
- 上视相机拍照产品A时,计算产品A的中心物理坐标和角度,以及产品A中心和吸嘴中心的实际偏差。
一次全拍流程:
这套贴装流程针对固定位置B和产品A分别可以被下视相机和上视相机整个拍下。
动作1:定位相机移至固定位置B中心处拍照
1、匹配结果-定位:计算下视相机图像中固定位置B(环形黑框)的中心与图像中心的像素偏差、固定位置B的角度
//m_SingelResult和m_DoubleResult为匹配计算到的固定位置B黑框左上和右下顶点 HTuple hv_centerRow = (m_DoubleResultRow + m_SingelResultRow) / 2; HTuple hv_centerCol = (m_DoubleResultCol + m_SingelResultCol) / 2; m_resultMatchOutLocalParam.m_localx = (double)(hv_centerCol - hv_ImgWidth.D() / 2.000); m_resultMatchOutLocalParam.m_localy = (double)(hv_centerRow - hv_ImgHeight.D() / 2.000); //求基板或盖子对角线连线与x轴正方向的夹角 HTuple angel, angelDeg; HTuple offsetX = m_DoubleResultCol - m_SingelResultCol; HTuple offsetY = m_DoubleResultRow - m_SingelResultRow; TupleAtan2(offsetY, offsetX, &angel); TupleDeg(angel, &angelDeg); m_resultMatchOutLocalParam.m_rotate = 180 - angelDeg.D(); pointLocateCenter.m_u = m_resultMatchOutLocalParam.m_rotate;
2、匹配结果-定位:固定位置B的中心与图像中心的像素偏差转换成机械坐标偏差
QPointF deviationLocateMark; deviationLocateMark1 = CameraCalibrateManager::getInstance()->getLocateCameraCaliPtr()-> transformImageDeltaToWorldDelta(QPointF(m_resultLocateMark1->m_localx, m_resultLocateMark1->m_localy));
3、匹配结果-定位:固定位置B物理中心坐标:拍照位置(即吸嘴中心位置-控制器读出) + 吸嘴中心与下视相机中心之间的安装距离(标定) + 下视相机图像中的固定位置B的中心与图像中心实际偏差(步骤2已求):
QPointF imageToWorldLocateMark1(snapLocateMark1Pos.nXCoord + ShiftCameraToPickerX + deviationLocateMark1.x(), snapLocateMark1Pos.nYCoord + ShiftCameraToPickerY + deviationLocateMark1.y());
动作2:吸嘴吸取产品A移至上视相机处拍照
1、匹配结果-uplook:计算上视相机图像中产品A(环形黑框)的像素中心与图像像素中心的像素偏差、产品A的角度
//m_SingelResult和m_DoubleResult为匹配计算到的产品A黑框左上和右下顶点 HTuple hv_centerRow = (m_DoubleResultRow + m_SingelResultRow) / 2; HTuple hv_centerCol = (m_DoubleResultCol + m_SingelResultCol) / 2; m_resultMatchOutLocalParam.m_localx = -(double)(hv_centerCol - hv_ImgWidth.D() / 2.000); m_resultMatchOutLocalParam.m_localy = -(double)(hv_centerRow - hv_ImgHeight.D() / 2.000); //求基板或盖子对角线连线与x轴正方向的夹角 HTuple angel, angelDeg; HTuple offsetX = m_DoubleResultCol - m_SingelResultCol; HTuple offsetY = m_DoubleResultRow - m_SingelResultRow; TupleAtan2(offsetY, offsetX, &angel); TupleDeg(angel, &angelDeg); m_resultMatchOutLocalParam.m_rotate = 180 - angelDeg.D(); pointUpLookCenter.m_u = m_resultMatchOutLocalParam.m_rotate;
注:这里加“-”是因为上视相机与下视相机安装方向相反
2、匹配结果-uplook:将产品A的像素中心与图像像素中心的偏差转换成实际机械坐标偏差
//m_resultUpLookMark1->m_localx:视觉传给软件的像素中心X方向偏差结果值 //m_resultUpLookMark1->m_localy:视觉传给软件的像素中心y方向偏差结果值 //CameraCalibrateManager::getInstance()->getUpLookCameraCaliPtr():标定的像素当量值 QPointF deviationUpLookMark1; deviationUpLookMark1 = CameraCalibrateManager::getInstance()->getUpLookCameraCaliPtr()-> transformImageDeltaToWorldDelta(QPointF(m_resultUpLookMark1->m_localx, m_resultUpLookMark1->m_localy));
3、匹配结果-uplook:相机在上视相机处拍照位置+产品A的中心与图像中心的实际偏差(步骤2)
QPointF imageToWorldUplookMark1; imageToWorldUplookMark1.setX(m_configure->getCalibrateParam().m_uplookCenter.centerPos.nXCoord + deviationUpLookMark1.x()); imageToWorldUplookMark1.setY(m_configure->getCalibrateParam().m_uplookCenter.centerPos.nYCoord + deviationUpLookMark1.y());
动作3:吸嘴吸取产品A贴合至固定位置B处
1、旋转吸嘴:校正固定位置B与产品A的角度偏差(pointLocateCenter.m_u和pointUpLookCenter.m_u分别由动作1-1,动作2-1计算得出)
//弧度制 负:顺时针旋转 正:逆时针旋转 _angleTowCameras为上视相机与下视相机的安装偏差 double TargetAngle = pointLocateCenter.m_u - pointUpLookCenter.m_u + _angleTowCameras;
2、移动吸嘴:计算吸嘴移至固定位置B需要移动的距离
由动作1-3计算出固定位置B的中心实际坐标、吸嘴在上视相机处旋转做完角度校正之后,可知此时吸嘴中心的实际坐标(由控制其读出),那么只要求出产品A中心与吸嘴中心的实际偏差值,补偿到吸嘴上中心的实际坐标上,即可计算出吸嘴到固定位置B处需要移动的距离,从而完成动作3的准确贴合。
1)固定位置B的中心实际坐标(由动作1-3计算得出)
pointLocateCenter.m_x = imageToWorldLocateMark1.x(); pointLocateCenter.m_y = imageToWorldLocateMark1.y();
2)下视相机与吸嘴之间的距离(标定得到)
double ShiftCameraToPickerX = _offsetCameraToPicker.m_x; double ShiftCameraToPickerY = _offsetCameraToPicker.m_y;
3)产品A中心的实际物理坐标:上视相机中心物理坐标(标定得到)+ 上视相机中心物理坐标与产品A中心的实际偏差(动作2-2计算得出)
lidMarkCenterPos.m_x = m_uplookCenter.centerPos.nXCoord + deviationUpLookMark1.x(); lidMarkCenterPos.m_y = m_uplookCenter.centerPos.nYCoord + deviationUpLookMark1.y();
4)产品A中心与吸嘴中心的实际偏差值:产品A中心 - 吸嘴中心(吸嘴在上视相机的拍照位置,控制器读出)
double x_lidOffset = lidMarkCenterPos.m_x - x_lidCameraPos; double y_lidOffset = lidMarkCenterPos.m_y - y_lidCameraPos;
5)吸嘴将产品A贴合至固定位置B处的需要移动的距离:固定位置B中心 - 产品A中心与吸嘴中心的偏差
double TargetX = pointLocateCenter.m_x - x_lidOffset; double TargetY = pointLocateCenter.m_y - y_lidOffset;
注:如果吸嘴恰好吸住了产品A的中心,那么最终贴合位置坐标就是:固定位置B中心。但是,实际情况是产品A中心与吸嘴中心存在偏差的,所以需要将这部分的偏差值补偿进去。
二次全拍流程:
这套贴装流程针对固定位置B和产品A由于太大,没办法被下视相机和上视相机整体拍下,所以采用拍照下视相机和上视相机分别拍固定位置B和产品A的左上角和右下角的方式来计算最终贴合位置的。
动作1:定位相机移至固定位置B左上角和右下角拍照
1、匹配结果-定位:计算固定位置B处的中心物理坐标和角度
//匹配结果-下视相机 —— 模板和图像 像素偏差(转换成机械坐标偏差) QPointF deviationLocateMark1, deviationLocateMark2; deviationLocateMark1 = CameraCalibrateManager::getInstance()->getLocateCameraCaliPtr()-> transformImageDeltaToWorldDelta(QPointF(m_resultLocateMark1->m_localx, m_resultLocateMark1->m_localy)); if (task.isDoublePosMark) { deviationLocateMark2 = CameraCalibrateManager::getInstance()->getLocateCameraCaliPtr()-> transformImageDeltaToWorldDelta(QPointF(m_resultLocateMark2->m_localx, m_resultLocateMark2->m_localy)); } //匹配结果-下视相机 —— 模板和图像 像素偏差 + 拍照位置 QPointF imageToWorldLocateMark1(snapLocateMark1Pos.nXCoord + deviationLocateMark1.x(), snapLocateMark1Pos.nYCoord + deviationLocateMark1.y()); QPointF imageToWorldLocateMark2(snapLocateMark2Pos.nXCoord + deviationLocateMark2.x(), snapLocateMark2Pos.nYCoord + deviationLocateMark2.y()); //匹配结果-下视相机 —— 产品A中心点坐标及与机械x轴角度(x1+x2 / 2),(y1+y2 / 2),0,arcTan((y2-y1)/(x2-x1)) MS_DevicePoint4D pointLocateCenter; if (task.isDoublePosMark) { pointLocateCenter.m_x = (imageToWorldLocateMark1.x() + imageToWorldLocateMark2.x()) / 2; pointLocateCenter.m_y = (imageToWorldLocateMark1.y() + imageToWorldLocateMark2.y()) / 2; pointLocateCenter.m_z = 0; pointLocateCenter.m_u = qAtan((imageToWorldLocateMark2.y() - imageToWorldLocateMark1.y()) / (imageToWorldLocateMark2.x() - imageToWorldLocateMark1.x())) + PI; pointLocateCenter.m_u = (pointLocateCenter.m_u * 180) / PI; }
动作2:吸嘴吸取产品A移至上视相机处,相机分别拍产品A的左上角和右下角
//匹配结果-uplook —— 模板和图像 像素偏差(转换成机械坐标偏差) QPointF deviationUpLookMark1, deviationUplookMark2; deviationUpLookMark1 = CameraCalibrateManager::getInstance()->getUpLookCameraCaliPtr()-> transformImageDeltaToWorldDelta(QPointF(m_resultUpLookMark1->m_localx, m_resultUpLookMark1->m_localy)); if (task.isDoubleUpLookMark) { deviationUplookMark2 = CameraCalibrateManager::getInstance()->getUpLookCameraCaliPtr()-> transformImageDeltaToWorldDelta(QPointF(m_resultUpLookMark2->m_localx, m_resultUpLookMark2->m_localy)); } //匹配结果-uplook —— 模板和图像 像素偏差 + 拍照位置 MS_DevicePoint lidUplookMark1Pos; MS_DevicePoint lidUplookMark2Pos; if (task.isDoubleUpLookMark) { lidUplookMark1Pos.m_x = m_configure->getCalibrateParam().m_uplookCenter.centerPos.nXCoord + deviationUpLookMark1.x()); lidUplookMark1Pos.m_y = (m_configure->getCalibrateParam().m_uplookCenter.centerPos.nYCoord + deviationUpLookMark1.y()); lidUplookMark2Pos.m_x = (m_configure->getCalibrateParam().m_uplookCenter.centerPos.nXCoord + deviationUplookMark2.x()); lidUplookMark2Pos.m_y = (m_configure->getCalibrateParam().m_uplookCenter.centerPos.nYCoord + deviationUplookMark2.y()); } //计算产品A和固定位置B的角度偏差并旋转吸嘴校正 //pointLocateCenter.m_u:固定位置B的角度 task.UCompensateValue:角度补偿值(固定) pointLocateCenter.m_u = pointLocateCenter.m_u - task.UCompensateValue; getTargetMotorAngel(pointLocateCenter, lidUplookMark1Pos, lidUplookMark2Pos, uplookSnapPos1, uplookSnapPos2, 0, targetMotorPos)) { //产品A-Mark1(左上角、先拍)实际坐标 double x_lidMarkPos_1 = lidUplookMark1Pos.m_x; double y_lidMarkPos_1 = lidUplookMark1Pos.m_y; //产品A-Mark2(右下角、后拍)实际坐标 double x_lidMarkPos_2 = lidUplookMark2Pos.m_x; double y_lidMarkPos_2 = lidUplookMark2Pos.m_y; //产品A左上角的拍照位置(吸嘴位置1,控制器读出) double x_lidCameraPos_1 = uplookSnapPos1.m_x; double y_lidCameraPos_1 = uplookSnapPos1.m_y; //产品A右下角的拍照位置(吸嘴位置2,控制器读出) double x_lidCameraPos_2 = uplookSnapPos2.m_x; double y_lidCameraPos_2 = uplookSnapPos2.m_y; //产品A移至拍照位置2(拍右下角)后,其左上角新的坐标位置 double x_lidMarkPosNew_1 = _lidMarkPos_1.m_x + x_lidCameraPos_2 - x_lidCameraPos_1; double y_lidMarkPosNew_1 = _lidMarkPos_1.m_y + y_lidCameraPos_2 - y_lidCameraPos_1; //产品A两角点连线与X轴方向的角度 double angle_lidCenter = qAtan((y_lidMarkPos_2 - y_lidMarkPosNew_1) / (x_lidMarkPos_2 - x_lidMarkPosNew_1)) + 3.1415926; //计算吸嘴需要旋转的角度偏差弧度转角度 angle_lidCenter = (angle_lidCenter / 3.1415926) * 180; double TargetAngle = pointLocateCenter.m_u - angle_lidCenter + _angleTowCameras; }
动作3:旋转吸嘴,校正角度偏差,并计算最终贴合位置
//相机中心-吸嘴中心间距(标定) MS_DevicePoint cameraSubtractSuck; cameraSubtractSuck.m_x = m_configure->getCalibrateParam().m_suckCenter.deviationX; cameraSubtractSuck.m_y = m_configure->getCalibrateParam().m_suckCenter.deviationY; //参数0指的是上视相机和下视相机安装角度偏差 getTargetMotorPos(pointLocateCenter,lidUplookMark1Pos, lidUplookMark2Pos,uplookSnapPos1, uplookSnapPos2,cameraSubtractSuck, 0, targetMotorPos); { //相机与吸嘴的安装位置差 double ShiftCameraToPickerX = cameraSubtractSuck.m_x; double ShiftCameraToPickerY = cameraSubtractSuck.m_y; //UpLook Mark1的拍照位置(吸嘴位置1,控制器读出) double x_lidCameraPos_1 = _lidCameraPos_1.m_x; double y_lidCameraPos_1 = _lidCameraPos_1.m_y; //UpLook Mark2的拍照位置(吸嘴位置2,控制器读出) double x_lidCameraPos_2 = _lidCameraPos_2.m_x; double y_lidCameraPos_2 = _lidCameraPos_2.m_y; //产品A-Mark1(左上角、先拍)实际坐标 double x_lidMarkPos_1 = _lidMarkPos_1.m_x; double y_lidMarkPos_1 = _lidMarkPos_1.m_y; //产品A-Mark2(左上角、先拍)实际坐标 double x_lidMarkPos_2 = _lidMarkPos_2.m_x; double y_lidMarkPos_2 = _lidMarkPos_2.m_y; //产品A有拍照位置1(左上角)移至拍照位置2(拍右下角)后,其产品-Mark1新的实际坐标位置 double x_lidMarkPosNew_1 = _lidMarkPos_1.m_x + x_lidCameraPos_2 - x_lidCameraPos_1; double y_lidMarkPosNew_1 = _lidMarkPos_1.m_y + y_lidCameraPos_2 - y_lidCameraPos_1; //计算产品A中心点实际坐标与角度 HTuple hv_ModelAngle; double x_lidCenter = (x_lidMarkPosNew_1 + x_lidMarkPos_2) / 2; double y_lidCenter = (y_lidMarkPosNew_1 + y_lidMarkPos_2) / 2; //在Mark2位置,此时吸嘴的位置 double x_PickerPos = x_lidCameraPos_2; double y_PickerPos = y_lidCameraPos_2; //产品A中心与吸嘴中心的偏差 double x_lidOffset = x_lidCenter - x_PickerPos; double y_lidOffset = y_lidCenter - y_PickerPos; //最终贴装位置 double TargetX = _substrateMotorPos.m_x + ShiftCameraToPickerX - x_lidOffset; double TargetY = _substrateMotorPos.m_y + ShiftCameraToPickerY - y_lidOffset; _targetMotorPos.m_x = TargetX; _targetMotorPos.m_y = TargetY; }
戳戳小手帮忙点个免费的赞和关注吧,嘿嘿。 |