背景
最近开发了一个六自由度机械臂调姿平台的控制软件,集成了API激光跟踪仪和KUKA机器人,实现了根据产品的测量位姿驱动仿真环境中模型并且实现模型间的碰撞检测。
其中KUKA机器人的控制可以参考笔者以前的博客:
https://blog.csdn.net/iamqianrenzhan/article/details/103115580
关于API激光跟踪仪的基本使用方法,将会在后续博客中介绍。本文主要是关于OpenCasCAD的使用,以及在qt中的配置。
OpenCasCAD简介
Open CASCADE(简称OCC)平台是由法国Matra Datavision公司开发的CAD/CAE/CAM软件平台。开源OCC对象库是一个面向对象C++类库,用于快速开发设计领域的专业应用程序。
OCC主要用于开发二维和三维几何建模应用程序,包括通用的或专业的计算机辅助设计CAD系统、制造或分析领域的应用程序、仿真应用程序或图形演示工具。OCC通过有机组织的C++库文件提供了六个模块。可视化模块作为OCC的核心部分,是可视化技术的具体体现。
OpenCasCAD的安装与自行编译
官网提供:可以在OpenCasCAD网站下载最新版
https://www.opencascade.com/content/latest-release
如果官网下载太慢,可以下载笔者上传的版本。
https://download.csdn.net/download/iamqianrenzhan/11982847
这个是安装版本,下载后可以直接使用。因为该安装版本也包含源码,也可以自行编译。自行编译参考笔者以前的博客:
https://blog.csdn.net/iamqianrenzhan/article/details/79558781
或者直接参考官网
https://www.opencascade.com/doc/occt-7.4.0/overview/html/occt_dev_guides__building_msvc.html
如果要用qt进行开发,最好在编译时把qt的版本修改为自己所用的版本。
Qt工程的建立
Qt工程主要参考了github上一个工程:
https://github.com/eryar/occQt
在pro文件中
QT += opengl
INCLUDEPATH += C:\OpenCASCADE-7.4.0-vc14-64\opencascade-7.4.0\inc
LIBS += -LC:\OpenCASCADE-7.4.0-vc14-64\opencascade-7.4.0\win64\vc14\lib
DEFINES += WNT
LIBS += -lTKernel -lTKMath -lTKV3d -lTKOpenGl \
-lTKBRep -lTKIGES -lTKSTL -lTKVRML -lTKSTEP -lTKSTEPAttr -lTKSTEP209 \
-lTKSTEPBase -lTKGeomBase -lTKGeomAlgo -lTKG3d -lTKG2d \
-lTKXSBase -lTKShHealing -lTKHLR -lTKTopAlgo -lTKMesh -lTKPrim \
-lTKCDF -lTKBool -lTKBO -lTKFillet -lTKOffset -lTKLCAF -lTKService
模型导入
新建一个管理模型导入导出的类:
class Standard_EXPORT ImportExport
{
public:
ImportExport();
public :
static void ReadIGES(const Handle(AIS_InteractiveContext)& anInteractiveContext,
Handle(AIS_Shape)& aShape);
static void ReadSTEP(const Handle(AIS_InteractiveContext)& anInteractiveContext,
Handle(AIS_Shape)& aShape);
};
目前这个类中只有两种格式模式的导入,后续会增加多种格式的导入和导出。
Stp文件导入
回传aShape参数是为了分别对导入的模型进行颜色设置,删除,移动等操作。
void ImportExport::ReadSTEP(const Handle(AIS_InteractiveContext)& anInteractiveContext,
Handle(AIS_Shape)& aShape)
{
//Handle(TopTools_HSequenceOfShape) aSequence = ImportExport::ReadSTEP();
Handle(TopTools_HSequenceOfShape) aSequence= new TopTools_HSequenceOfShape();
QString fileName = QFileDialog::getOpenFileName(nullptr,
QObject::tr("read a stp file"),
"E:",
QObject::tr("STEP Files(*.stp *.step)"));
qDebug() << fileName;
if(fileName.isNull())
{
qDebug() << "没有选择文件";
std::cout << "没有选择文件" <<std::endl;
return;
}
std::string test = fileName.toStdString(); //必须要把toStdString和data函数分开写。
Standard_CString aFileName = test.data();
//IFSelect_ReturnStatus ReturnStatus = ReadSTEP(aFileName,aSequence);
aSequence->Clear();
// create additional log file
STEPControl_Reader aReader;
IFSelect_ReturnStatus status = aReader.ReadFile(aFileName);
if(status !=IFSelect_RetDone)
{
switch (status)
{
case IFSelect_RetError:
std::cout << "Not a valid Step file" << std::endl;
break;
case IFSelect_RetFail :
std::cout << "Reading has failed" << std::endl;
break;
case IFSelect_RetVoid :
std::cout << "Nothing to transfer" << std::endl;
break;
default:
break;
}
return;
}
aReader.WS()->MapReader()->SetTraceLevel(2); // increase default trace level
Standard_Boolean failsonly = Standard_False;
aReader.PrintCheckLoad(failsonly, IFSelect_ItemsByEntity);
// Root transfers
Standard_Integer nbr = aReader.NbRootsForTransfer();
aReader.PrintCheckTransfer (failsonly, IFSelect_ItemsByEntity);
for ( Standard_Integer n = 1; n<=nbr; n++)
{
Standard_Boolean ok = aReader.TransferRoot(n);
}
// Collecting resulting entities
Standard_Integer nbs = aReader.NbShapes();
if (nbs == 0) return;
for (Standard_Integer i=1; i<=nbs; i++)
aSequence->Append(aReader.Shape(i));
//Handle_AIS_Shape aShape;
if (!aSequence.IsNull())
{
for(int i=1;i<= aSequence->Length();i++)
{
aShape = new AIS_Shape(aSequence->Value(i));
anInteractiveContext->SetColor(aShape,Quantity_NOC_YELLOW,Standard_False);
anInteractiveContext->SetMaterial(aShape,Graphic3d_NOM_PLASTIC,Standard_False);
anInteractiveContext->SetDisplayMode(aShape,1,Standard_False);
//anInteractiveContext->SetTransparency(aShape,0.2,Standard_False);
anInteractiveContext->Display(aShape, Standard_False);
}
}
}
Igs文件导入
void ImportExport::ReadIGES(const Handle(AIS_InteractiveContext)& anInteractiveContext,
Handle(AIS_Shape)& aShape)
{
//Handle(TopTools_HSequenceOfShape) aSequence = ImportExport::ReadIGES();
Handle(TopTools_HSequenceOfShape) aSequence = new TopTools_HSequenceOfShape();
QString fileName = QFileDialog::getOpenFileName(nullptr,
QObject::tr("read a igs file"),
"E:",
QObject::tr("IGES Files(*.iges *.igs)"));
if(fileName.isNull())
{
qDebug() << "没有选择文件";
std::cout << "没有选择文件" << std::endl;
return;
}
qDebug() << fileName;
std::string test = fileName.toStdString(); //必须要把toStdString和data函数分开写。
Standard_CString aFileName = test.data();
//Standard_Integer status = ReadIGES(aFileName,aSequence);
IGESControl_Reader Reader;
Standard_Integer status = Reader.ReadFile(aFileName);
if (status != IFSelect_RetDone)
{
std::cout << "Error : The file is not read" << std::endl;
qDebug() << "Error : The file is not read";
return;
}
Reader.TransferRoots();
TopoDS_Shape tShape = Reader.OneShape();
aSequence->Append(tShape);
//Handle_AIS_Shape aShape;
if (!aSequence.IsNull())
{
for(int i=1;i<= aSequence->Length();i++)
{
aShape = new AIS_Shape(aSequence->Value(i));
anInteractiveContext->SetColor(aShape,Quantity_NOC_YELLOW,Standard_False);
anInteractiveContext->SetMaterial(aShape,Graphic3d_NOM_PLASTIC,Standard_False);
anInteractiveContext->SetDisplayMode(aShape,1,Standard_False);
//anInteractiveContext->SetTransparency(aShape,0.2,Standard_False);
anInteractiveContext->Display(aShape, Standard_False);
}
}
}
位姿驱动
这里提供两种方法,一种是提供12个参数的其次RT矩阵,一个是提供6个参数的ABCXYZ参数。
void OccView::SetModelLocation(Handle(AIS_Shape)& aShape,gp_Trsf trsf)
{
Handle_AIS_InteractiveObject Current(aShape);
if(!Current.IsNull())
{
myContext->SetLocation(Current,trsf);
myContext->Update(Current,Standard_True); //等价于这句话 myContext->UpdateCurrentViewer();//窗口更新
}
}
//设置当前对象的位置
void OccView::OccView::SetModelLocation_Matrix(Handle(AIS_Shape)& aShape, double* matrixTemp)
{
gp_Trsf trsf;
trsf.SetValues(matrixTemp[0],matrixTemp[1],matrixTemp[2], matrixTemp[3],
matrixTemp[4],matrixTemp[5],matrixTemp[6], matrixTemp[7],
matrixTemp[8],matrixTemp[9],matrixTemp[10],matrixTemp[11]);
SetModelLocation(aShape,trsf);
}
//通过YPR角度设置当前对象的位置
void OccView::SetModelLocation_Euler(Handle(AIS_Shape)& aShape, double* pTemp)
{
pTemp[0] = pTemp[0]/180*M_PI;
pTemp[1] = pTemp[1]/180*M_PI;
pTemp[2] = pTemp[2]/180*M_PI;
//设置欧拉角
gp_Trsf aTrsf_Rotation;
gp_Quaternion aQ;
aQ.SetEulerAngles(gp_YawPitchRoll,pTemp[0],pTemp[1],pTemp[2]);
aTrsf_Rotation.SetRotation(aQ);
//设置平移向量
gp_Trsf aTrsf_Translation;
gp_Vec theVectorOfTrans(pTemp[3],pTemp[4],pTemp[5]);
aTrsf_Translation.SetTranslation(theVectorOfTrans);
gp_Trsf trsf = aTrsf_Translation * aTrsf_Rotation;
SetModelLocation(aShape,trsf);
}
碰撞检测
这里碰撞检测使用bool交操作,对两个模型进行bool交操作,根据交集结果进行是否碰撞判断。
bool OccView::IsCommon()
{
//从AIS类型获得TOPO类型
if(m_AISTargetBody.IsNull() || m_AISMoveBody.IsNull())
{
qDebug() << "no model imported";
return false;
}
m_TppoTargetBody = m_AISTargetBody->Shape();
m_TopoMoveBody = m_AISMoveBody->Shape();
//没有实时性要求,采用bool求交的方法
Standard_Boolean bRunParallel;
Standard_Real aFuzzyValue;
BRepAlgoAPI_Common aBuilder;
// perpare the arguments
TopTools_ListOfShape aLS;
TopTools_ListOfShape aLT;
aLS.Append(m_TppoTargetBody_cal); //xx m_TppoTargetBody
aLT.Append(m_TopoMoveBody_cal); //yy m_TopoMoveBody
bRunParallel=Standard_True;
aFuzzyValue=2.1e-5;
// set the arguments
aBuilder.SetArguments(aLS);
aBuilder.SetTools(aLT);
aBuilder.SetRunParallel(bRunParallel);
aBuilder.SetFuzzyValue(aFuzzyValue);
// run the algorithm
aBuilder.Build();
if (aBuilder.HasErrors())
{
std::ofstream fout;
fout.open("errormessage.txt",std::ios_base::app);
aBuilder.DumpErrors(fout);
fout.flush();
fout.close();
qDebug() << "error";
return false;
}
// result of the operation aR
const TopoDS_Shape& aR=aBuilder.Shape();
Bnd_Box B;
BRepBndLib::Add(aR, B);
double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
if(!B.IsVoid())
{
B.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
}
int nb = aR.NbChildren();
if(!B.IsVoid()) //交集部分的最小包围box存在,则说明碰撞
{
std::cout << "common" << std::endl;
return true;
}
else
{
std::cout << "no common" << std::endl;
return false;
}
}
最终效果展示