.pro文件中需要加入这句才能找到对应的头文件
QT +=network
#include
#include
//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)
为了能够实时获取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)
留意最后一句话,官方文档中说明我们收到的信息大小应该为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"