robot-program-offline-master(五轴机器人离线仿真)
删除代码项目里面的ui开头的文件重新编译就不会出错;
模型
左边导航
Dockwidget
点击联调按钮加载analysiswidget
绘图使用customplot
STLView 解析STL文件 使用多线程解析
顶点
//三角形片类,主要由三个顶点坐标和一个法向量组成
class MyTriangle
{
public:
MyPoint normal,vertex1,vertex2,vertex3;
};
//解析STL文件生成triangleVector数组
bool STLView::readSTL(const QString &fname){
QFile file(fname);
if ( file.open(QFile::ReadOnly | QFile::Text)) //仅读和文字
{
QTextStream in( &file );
MyTriangle triangle;
QString line;
int trianglePartNums = 0;
while ( !in.atEnd() )
{
line = in.readLine();
QVector<QString> words;
//分割一行,生成words字符串数组返回字符个数
int num = CommonFun::splitString(line,' ',words);
//解析一行
for(int i = 0; i < num; i++)
{
//集齐4个参数,添加一个三角片
if(trianglePartNums == 4)
{
triangleVector.push_back(triangle);
trianglePartNums = 0;
}
if(words[i] == "normal")
{
triangle.normal.x = words[i+1].toDouble();
triangle.normal.y = words[i+2].toDouble();
triangle.normal.z = words[i+3].toDouble();
trianglePartNums++;
break;
}
if(words[i] == "vertex")
{
if(trianglePartNums == 1)
{
triangle.vertex1.x = words[i+1].toDouble();
triangle.vertex1.y = words[i+2].toDouble();
triangle.vertex1.z = words[i+3].toDouble();
}
if(trianglePartNums == 2)
{
triangle.vertex2.x = words[i+1].toDouble();
triangle.vertex2.y = words[i+2].toDouble();
triangle.vertex2.z = words[i+3].toDouble();
}
if(trianglePartNums == 3)
{
triangle.vertex3.x = words[i+1].toDouble();
triangle.vertex3.y = words[i+2].toDouble();
triangle.vertex3.z = words[i+3].toDouble();
}
trianglePartNums++;
break;
}
}
}
file.close();
return true;
}
else
{
qWarning() << fname << ": STL 文件打开失败";
return false;
}
}
void STLView::run()
{
switch(type)
{
case LanxunCoordRobot:
lanxunCoordView[n].readSTL(QString(":/stl/lanxunCoordRobot/%1.STL").arg(n+1));
break;
case Lanxun5JointRobot:
lanxun5JointView[n].readSTL(QString(":/stl/lanxun5JointRobot/%1.STL").arg(n+1));
break;
case Lanxun6JointRobot:
lanxun6JointView[n].readSTL(QString(":/stl/lanxun6JointRobot/%1.STL").arg(n));
break;
}
}
OpenglWidget 使用gllist生成
void OpenglWidget::makeObject()
{
qDebug()<<"makeObject";
double div = scaleNum;
int listNum = 0;
//如果没有工件,就少一个显示列表
/*if(artifacts == NULL){
listNum = num;
}else{
listNum = num + 1;
}*/
listNum=(!artifacts)?num:++num;
/**************************************************************************
OpenGL中用正整数来区分不同的显示列表,为防止重复定义已经存在的显示列表号,
使用glGenLists函数来自动分配一个没有被使用过的显示列表编号。
参数指定要分配几个显示列表,返回值是被分配的显示列表中的最小编号,若返回0表示分配失败
**************************************************************************/
gllist[0] = glGenLists(listNum);
for(int i = 1; i < listNum; i++){
gllist[i] = gllist[0] + i;
}
for(int n = 0; n < num; n++){
//开始装入:第一个参数标示当前正在操作的显示列表号
//第二个参数有两种取值GL_COMPILE和GL COMPILE_AND_EXECUTE,
//前者声明当前显示列表只是装入相应OpenGL语句,不执行;后者表示在装入的同时,执行一遍当前显示列表。
glNewList(gllist[n], GL_COMPILE );
qglColor(color[n]);
glBegin(GL_TRIANGLES);
for(int i = 0; i < (view[n]).triangleVector.size();i++) {
glNormal3f((view[n]).triangleVector[i].normal.x , (view[n]).triangleVector[i].normal.y ,(view[n]).triangleVector[i].normal.z);
glTexCoord2f(0, 0);
glVertex3f((view[n]).triangleVector[i].vertex1.x / div ,(view[n]).triangleVector[i].vertex1.y/ div ,
(view[n]).triangleVector[i].vertex1.z / div );
glTexCoord2f( 0.5, 1.0 );
glVertex3f((view[n]).triangleVector[i].vertex2.x / div ,(view[n]).triangleVector[i].vertex2.y / div,
(view[n]).triangleVector[i].vertex2.z / div );
glTexCoord2f( 1.0, 0 );
glVertex3f((view[n]).triangleVector[i].vertex3.x / div,(view[n]).triangleVector[i].vertex3.y / div ,
(view[n]).triangleVector[i].vertex3.z / div );
}
glEnd();
glEndList();
}
if(artifacts){
//创建显示列表并付给list
glNewList(gllist[num], GL_COMPILE );
qglColor(color[num]);
/**************************************************************************
* glNormal3f设置当前法线数组
* glTexCoord2f绘制图形时指定纹理的坐标,为了将纹理正确的映射到四边形上,必须将纹理的四个角映射到四边形的四个角
* glVertex3f指定3D顶点,一般与glTexCoord2f同时使用,先纹理后顶点
* glBegin(GL_TRIANGLES)多组独立填充三角形
**************************************************************************/
glBegin(GL_TRIANGLES);
for(MyTriangle it: artifacts->triangleVector) {
glNormal3f(it.normal.x,it.normal.y ,it.normal.z);
//左下角的纹理与顶点映射
glTexCoord2f(0, 0);
glVertex3f(it.vertex1.x / div ,it.vertex1.y/ div ,it.vertex1.z / div );
//中间点
glTexCoord2f( 0.5, 1.0 );
glVertex3f(it.vertex2.x / div ,it.vertex2.y / div,it.vertex2.z / div );
//右下角
glTexCoord2f( 1.0, 0 );
glVertex3f(it.vertex3.x / div,it.vertex3.y / div ,it.vertex3.z / div );
/*
for(int i = 0; i < artifacts->triangleVector.size();i++) {
glNormal3f(artifacts->triangleVector[i].normal.x , artifacts->triangleVector[i].normal.y ,artifacts->triangleVector[i].normal.z);
//左下角的纹理与顶点映射
glTexCoord2f(0, 0);
glVertex3f(artifacts->triangleVector[i].vertex1.x / div ,artifacts->triangleVector[i].vertex1.y/ div ,
artifacts->triangleVector[i].vertex1.z / div );
//中间点
glTexCoord2f( 0.5, 1.0 );
glVertex3f(artifacts->triangleVector[i].vertex2.x / div ,artifacts->triangleVector[i].vertex2.y / div,
artifacts->triangleVector[i].vertex2.z / div );
//右下角
glTexCoord2f( 1.0, 0 );
glVertex3f(artifacts->triangleVector[i].vertex3.x / div,artifacts->triangleVector[i].vertex3.y / div ,
artifacts->triangleVector[i].vertex3.z / div );
*/
}
glEnd();
glEndList();
}
qDebug()<<"makeObject";
}
OpenglWidget 按键 鼠标 滚轮
通过改变关节角度重新绘制
OpenglWidget 文字
示教窗口
示教窗口 列表
列表右键菜单
示教窗口 点动 逆解求关节角度
机器人正逆解
SimulationParse
Opengl绘制(使用GLlist)
数据包解析
//提取数据,数据包结构包括,(指令,指令长度,6个坐标,8关节值)
int Parser::getValue(const QStringList & list)
{
if(list.size() < 2)
return -1;
if(list.size() != list[1].toInt())
return -1;
if(list[0] == "UC"){
cmd = CoordJoint;
for(int i=2;i<16;++i){
if(i<8){
coord[i-2] = list[i].toDouble();
}else{
joint[i-8] = list[i].toDouble();
}
}
// for(int i = 2;i < 8;i++){
// coord[i - 2] = list[i].toDouble();
// }
// for(int i = 8; i < 16; i++){
// joint[i - 8] = list[i].toDouble();
// }
return 0;
}
if(list[0] == "M7"){
qDebug() << "开火";
cmd = OpenFire;
return 0;
}
if(list[0] == "M8"){
qDebug() << "关火";
cmd = CloseFire;
return 0;
}
if(list[0] == "EE"){
qDebug() << "连接";
cmd = ConnectCmd;
return 0;
}
else
return -1;
}
数据发送(使用套接字)
数据曲线绘制
矩阵运算(机器人正逆解)
各种旋转矩阵
逆运动学结算由解析解公式代入即可
G代码解析(插补运算,圆弧等分)