基于Qt与C++控制真实UR3机器人

基于Qt与C++控制真实UR3机器人

环境配置

.pro文件中需要加入这句才能找到对应的头文件

QT +=network
#include 
#include 

一、定义一个客户端与UR3机器人通讯

//widget.h
QTcpSocket *client_;
//widget.cpp
client_ = new QTcpSocket(this);

二、客户端与服务器连接

IP地址需要看机器人示教器实际的地址

我的机械臂的IP地址为:192.168.1.10

通过网线连接电脑与UR3控制柜,在cmd中输入

ping  192.168.1.10

可以检查网络连接的通断

端口号不是随便定的,是根据以下列表选择其中一个端口都能够与UR3机器人建立连接。

端口图出处Overview of client interfaces - 21744 (universal-robots.cn)

基于Qt与C++控制真实UR3机器人_第1张图片

为了能够实时获取UR3机器人的信息,将端口号设置为30003

//widget.h
QString IP_;
QString port_;
//widget.cpp
unsigned short port = port_.toUShort();  //注意传入的port的类型
client_->connectToHost(QHostAddress(IP_), port);  //注意传入IP的类型
if(!client_->waitForConnected(1000))  //建立连接,连接尝试时间为1000ms
{
    QMessageBox::warning(this, "连接失败" , "机器人连接失败");
    return;
}

没有弹出对话框即为成功连接

三、写指令

指令详情参考《脚本手册_中文_3.1.pdf》

下列例程以关节角驱动为例

void Widget::setJoints(std::vector<double> angle){
    if (angle.size()!=6){
        return;   //输入有误
    }
    /*
    ......
    */
    QString sendMsg;  //传递消息的类型
    sendMsg = QString("movej([%1,%2,%3,%4,%5,%6],a=%7,v=%8)\n").arg(
                      QString::number(theta1,'f',5),
                      QString::number(theta2,'f',5),
                      QString::number(theta3,'f',5),
                      QString::number(theta4,'f',5),
                      QString::number(theta5,'f',5),
                      QString::number(theta6,'f',5),
                      QString::number(move_a_,'f',3),
                      QString::number(move_v_,'f',3));//a=1,v=1
    client_->write(sendMsg.toUtf8());  //重要步骤(写指令)
}

四、断开连接

client_->close();

五、实时读取机器人状态

通过30003端口与UR3服务器连接后,UR3机器人会以125HZ的频率向客户端发送消息,消息里面包含了消息的大小,机器人当前的状态等信息,具体的信息格式如下图,这个图的出处Remote Control Via TCP/IP - 16496 (universal-robots.cn)

基于Qt与C++控制真实UR3机器人_第2张图片基于Qt与C++控制真实UR3机器人_第3张图片
基于Qt与C++控制真实UR3机器人_第4张图片
留意最后一句话,官方文档中说明我们收到的信息大小应该为1140个字节,但我们实际上收到的信息大小可能比1140字节小,但就算是大小小于1140字节,上述图中的信息结构依旧成立,即我们仍可将信息按照以上结构进行解析。

下列为解析代码

首先拿了最前面4个字节的数据进行信息流校对,前四个字节代表了收到的信息的大小,类型是int,我实际拿到的信息为1044字节,所以验证一下拿到信息的大小是否真的等于1044字节,如果是则继续解析。

然后拿中间的关节角的实时位置,然后把十六进制转为double类型,因为关节角是以弧度表示的,×180/PI转为角度。

//接收信息
void Widget::readMsg(){
    //std::cout<<"recv_msg!!!!"<
    QByteArray buffer = client_->readAll();
    //提取接收信息的帧
    QByteArray data_size = extractInformation(buffer,0,3);  //获取接收信息的size,用来校对信息是否有效
    QString msg = data_size.toHex();
    int recv_size = msg.toInt(NULL,16);
    if (recv_size == 1044){  //确保接收到完整的信息流再处理
        current_joint_.clear();
        //提取实际六轴关节角的角度
        QByteArray joint1_a = extractInformation(buffer,252,259);
        double joint1 = QByteArray2Double(joint1_a);
        QByteArray joint2_a = extractInformation(buffer,260,267);
        double joint2 = QByteArray2Double(joint2_a);
        QByteArray joint3_a = extractInformation(buffer,268,275);
        double joint3 = QByteArray2Double(joint3_a);
        QByteArray joint4_a = extractInformation(buffer,276,283);
        double joint4 = QByteArray2Double(joint4_a);
        QByteArray joint5_a = extractInformation(buffer,284,291);
        double joint5 = QByteArray2Double(joint5_a);
        QByteArray joint6_a = extractInformation(buffer,292,299);
        double joint6 = QByteArray2Double(joint6_a);
        current_joint_.push_back(joint1*180/PI);
        current_joint_.push_back(joint2*180/PI);
        current_joint_.push_back(joint3*180/PI);
        current_joint_.push_back(joint4*180/PI);
        current_joint_.push_back(joint5*180/PI);
        current_joint_.push_back(joint6*180/PI);
    }
    //qDebug()<
}

//从接收的十六进制信息流中提取信息
QByteArray Widget::extractInformation(QByteArray recv,int start,int end){
    QByteArray output;
    for (int i=start;i<=end;i++){
        output.append(recv[i]);
    }
    return output;
}


//十六进制的二进制流转double
double Widget::QByteArray2Double(QByteArray input){
    QString input_m = input.toHex();
    qulonglong hex_long = input_m.toULongLong(nullptr, 16);
    double hex_d = *(double*)&hex_long;
    return hex_d;
}

六、机器人示教

void Widget::TurnOnTeachMode(){
    //开启示教模式
    QString sendMsg;
    sendMsg = QString("def free():\n\t" \
                      "teach_mode()\n" \
                      "while (True) :\n\t\t" \
                      "sync()\n\t" \
                      "end\n"\
                      "end\n");  //设置示教模式
    client_->write(sendMsg.toUtf8());
}


void Widget::TurnOffTeachMode(){
    //关闭示教模式
    QString sendMsg;
    sendMsg = QString("end_teach_mode()\n");  //设置示教模式
    client_->write(sendMsg.toUtf8());
}

让机器人进入示教模式或者免驱动模式的方式稍微有些不同,根据脚本手册

定义一个函数,在函数中发送teach_mode()让机器人进入示教模式,然后创建线程在循环内保持示教模式

而退出示教模式只需要发送一次"end_teach_mode()\n"
让机器人进入示教模式或者免驱动模式的方式稍微有些不同,根据脚本手册

定义一个函数,在函数中发送teach_mode()让机器人进入示教模式,然后创建线程在循环内保持示教模式

而退出示教模式只需要发送一次"end_teach_mode()\n"

你可能感兴趣的:(qt,c++,机器人)