Qt Modbus庫使用,並作爲ROS節點發布話題及程序CMakelist編寫

在QT框架下,使用QModbus庫實現了客戶端程序對埃夫特機器人ER50C10A實時讀取當前關節角度、笛卡爾座標值等參數。

同時作爲ROS節點發布到話題/joint_states中。

程序邏輯:

啓動Modbus -- 連接--發送讀取請求 --等待qt返回讀取完畢信號觸發槽函數讀取 --讀取並顯示

其中,連接與發送請求等各部分需要寫到不同的槽函數中,否則會報錯modbus未連接。

程序代碼:(截取部分)

主程序:初始化ROS節點、TOPIC、qt對象

int main(int argc, char *argv[])
{
	ros::init ( argc, argv, "test_modbus" );
	ros::NodeHandle n;
	ros::Publisher Cmd = n.advertise<sensor_msgs::JointState> ("/joint_states", 1024 );
	QApplication a(argc, argv);
	ModbusTcpClient w;
	w.publisher = &Cmd;
	w.show();
	return a.exec();
}

QModbus相關程序:

ModbusTcpClient::ModbusTcpClient(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::ModbusTcpClient), m_holdingRegisters(20)
{
	ui->setupUi(this);
	modbusDevice = new QModbusTcpClient(this);
	ui->lineEdit->setText(QLatin1Literal("192.168.0.103:502"));
	connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {statusBar()->showMessage(modbusDevice->errorString(), 5000);});
	connect(modbusDevice, &QModbusClient::stateChanged,this, &ModbusTcpClient::onStateChanged);
	connect(this, SIGNAL(readonce()),this,SLOT(on_readButton_clicked()));
}
ModbusTcpClient::~ModbusTcpClient()
{
	if (modbusDevice)
		modbusDevice->disconnectDevice();
	delete modbusDevice;
	delete ui;
}
void ModbusTcpClient::on_connectButton_clicked()
{
	emit Scan();
	if (!modbusDevice)
		return;
	statusBar()->clearMessage();

	if (modbusDevice->state() != QModbusDevice::ConnectedState) {
		const QUrl url = QUrl::fromUserInput(ui->lineEdit->text());
		modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
		modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());

		modbusDevice->setTimeout(1000);
		modbusDevice->setNumberOfRetries(3);
		if (!modbusDevice->connectDevice()) {
			statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
		}
	}
	else {
		modbusDevice->disconnectDevice();
	}
}
void ModbusTcpClient::onStateChanged(int state)
{

	if (state == QModbusDevice::UnconnectedState)
		ui->connectButton->setText(tr("Connect"));
	else if (state == QModbusDevice::ConnectedState)
		ui->connectButton->setText(tr("Disconnect"));
}
void ModbusTcpClient::on_readButton_clicked()
{
	
		if (!modbusDevice)
		return;
		statusBar()->clearMessage();
		QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 12, 12);
		if (auto *reply = modbusDevice->sendReadRequest(readUnit, 1)) {
			if (!reply->isFinished())
				connect(reply, &QModbusReply::finished, this, &ModbusTcpClient::readReady);
			else
				delete reply; // broadcast replies return immediately
		}
		else {
			statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
		}
		
	
	}
void ModbusTcpClient::readReady()
{
	std::vector<double>	jointPos;
	jointPos.clear();
	auto reply = qobject_cast<QModbusReply *>(sender());
	if (!reply)
		return;
	if (reply->error() == QModbusDevice::NoError) {
		const QModbusDataUnit unit = reply->result();
		for (uint i = 0; i < unit.valueCount(); i++) {
			const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress())
				.arg(QString::number(unit.value(i),
					unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
			);
			
		}
		ui->textBrowser->insertPlainText("機器人當前位姿:");
		for (int q = 0; q < 6; q++)
		{
			poses.clear();
			
			QString number1 = QString::number(unit.value(2 * q + 1), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16);
			
			if (number1.length() < 4)
			{
				poses.append(QString::number(0));
			}
			poses.append(QString::number(unit.value(2 * q + 1), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
			
			QString number2 = QString::number(unit.value(2 * q), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16);
			if (number2.length() < 4)
			{
				poses.append(QString::number(0));
			}
			poses.append(QString::number(unit.value(2 * q), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
			pose[q] = BytesToFloat(poses);
			qDebug() << poses;
			jointPos.push_back(pose[q]/180*3.1415926);
			ui->textBrowser->insertPlainText(QString::number(pose[q]));
			ui->textBrowser->insertPlainText(" ");
		}
		ui->textBrowser->insertPlainText("\n");
		ui->textBrowser->moveCursor(QTextCursor::End);
		jointState.header.stamp = ros::Time::now();
        jointState.name = j_names;
        jointState.position = jointPos;
		publisher->publish(jointState);
	}

	else if (reply->error() == QModbusDevice::ProtocolError) {
		statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
			arg(reply->errorString()).
			arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
	}
	else {
		statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
			arg(reply->errorString()).
			arg(reply->error(), -1, 16), 5000);
	}

	reply->deleteLater();
	emit readonce();
}
void ModbusTcpClient::Display()
{
	ui->textBrowser->insertPlainText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss "));
	ui->textBrowser->insertPlainText("機器人當前位姿:");
	ui->textBrowser->moveCursor(QTextCursor::End);
}

CMakeList編寫:

cmake_minimum_required(VERSION 2.8.3)
project(test_modbus)
add_compile_options(-std=c++11)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(Qt5SerialBus_DIR "/home/xxx/Qt5.8.0/5.8/gcc_64/lib/cmake/Qt5SerialBus")


find_package(catkin REQUIRED roscpp std_msgs sensor_msgs)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5SerialBus REQUIRED)


catkin_package()


include_directories(${catkin_INCLUDE_DIRS})
include_directories(${Qt5SerialBus_INCLUDE_DIRS})
add_definitions(${Qt5SerialBus_DEFINITIONS})
qt5_wrap_ui( UIC src/test_modbus.ui)

add_executable(testmodbus src/main.cpp src/test_modbus.cpp src/test_modbus.h src/test_modbus.ui) 
target_link_libraries(testmodbus ${catkin_LIBRARIES} Qt5::Widgets Qt5::SerialBus)

結果

QT界面

ROS-joint_states話題

ROS-機器人仿真界面

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章