在ROS2中,一般都需要通过 ros2 run 指令或 ros2 launch指令去完成节点的运行,那么我可不可以通过ui界面去控制节点的开始与关闭呢?
上一章节讲了QT多线程应用与ROS2的topic通讯,实现了后台线程运行,以及按键控制其运行的操作。
那么这一章我想通过QT的ui界面去完成python节点与C++节点的通讯以及启动:
开始之前我需要先说明两个在linux中的常用指令:
第一行是用来查找linux正在运行的线程
ps aux | grep <线程的名字>
第二行是用来杀死正在运行的线程,-f 参数告诉 pkill 根据完整的命令行来匹配进程,而不是匹配进程名。
pkill -f <线程的名字>
使用进程名匹配 (-n):
pkill -n "<进程名>"
-n 选项允许你通过进程名来匹配和杀死进程。
使用进程名匹配 (-n):
pkill -n "<进程名>"
-n 选项允许你通过进程名来匹配和杀死进程。
使用用户匹配 (-u):
pkill -u "<用户名>"
使用 -u 选项可以杀死指定用户的所有进程。
使用进程组ID匹配 (-g):
pkill -g "<进程组ID>"
-g 选项允许你通过进程组ID来杀死进程组中的所有进程。
使用进程ID匹配 (-p):
pkill -p "<进程ID>"
使用 -p 选项可以直接通过进程ID来杀死进程。
组合使用多个选项:
你可以组合使用多个选项来进一步细化匹配条件。例如,如果你只想杀死属于特定用户并且具有特定进程名的进程,可以这样做:
pkill -u <用户名> -n <进程名>
使用正则表达式 (-e):
pkill -e "<正则表达式>"
-e 选项允许你使用扩展的正则表达式来匹配进程名。
使用信号 (-<signal>):
默认情况下,pkill 使用 SIGTERM 信号来终止进程。你可以使用 -<signal> 选项来指定其他信号,例如 -9 表示 SIGKILL:
pkill -9 -f "<完整的命令行>"
第一步先创建了一个CommandExecutor.h类,该类可以在后台进行,通过对后台的命令符号进行处理,便可以直接对终端可操作的内容进行操作。
#ifndef COMMANDEXECUTOR_H
#define COMMANDEXECUTOR_H
#include <QObject>
#include <QThread>
#include <QProcess>
class CommandExecutor : public QObject
{
Q_OBJECT
public:
explicit CommandExecutor(QObject *parent = nullptr);
~CommandExecutor();
public slots:
void executeCommand(const QString &command);
void stopCommand();
signals:
void commandOutput(const QString &output);
void commandError(const QString &error);
private slots:
void readStandardOutput();
void readStandardError();
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
QProcess *process;
QThread workerThread;
};
#endif // COMMANDEXECUTOR_H
CommandExecutor.cpp文件
readStandardError:但是这种,只能传达这种消息,Output不可以传出这种消息
#include "commandexecutor.h"
#include <QProcess>
#include <QDebug>
CommandExecutor::CommandExecutor(QObject *parent) : QObject(parent), process(new QProcess)
{
// Move this object to the worker thread
// 注册QProcess::ExitStatus类型,注册为一个可使用的元类型(meta-type)
qRegisterMetaType<QProcess::ExitStatus>();
this->moveToThread(&workerThread);
// Connect QProcess signals to slots
connect(process, &QProcess::readyReadStandardOutput, this, &CommandExecutor::readStandardOutput);
connect(process, &QProcess::readyReadStandardError, this, &CommandExecutor::readStandardError);
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &CommandExecutor::processFinished);
}
CommandExecutor::~CommandExecutor()
{
stopCommand();
workerThread.quit();
workerThread.wait();
delete process;
}
void CommandExecutor::executeCommand(const QString &command)
{
workerThread.start();
if (process->state() != QProcess::NotRunning) {
qWarning() << "Process is already running. Please stop it first.";
return;
}
QStringList arguments = command.split(" ");
QString program = arguments.takeFirst();
process->start(program, arguments);
}
void CommandExecutor::stopCommand()
{
// if (process->state() != QProcess::NotRunning) {
process->terminate();
// if (!process->waitForFinished(3000)) {
process->kill();
process->waitForFinished();
//}
// }
}
void CommandExecutor::readStandardOutput()
{
QByteArray output = process->readAllStandardOutput();
emit commandOutput(QString::fromLocal8Bit(output));
}
void CommandExecutor::readStandardError()
{
QByteArray error = process->readAllStandardError();
emit commandError(QString::fromLocal8Bit(error));
}
void CommandExecutor::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
QString result = QString("Process finished with exit code %1, exit status %2")
.arg(exitCode)
.arg(exitStatus == QProcess::NormalExit ? "NormalExit" : "CrashExit");
emit commandOutput(result);
}
在main函数中,需要创建一下,然后再去执行:
CommandExecutor executor;
executor.executeCommand("ros2 run village_li li4_node");
connect(ui->pushButton_1, &QPushButton::clicked,this,[&](){rosWorker->stop();executor.stopCommand();executor.executeCommand("pkill -f li4_node");});
但是在主页面回调函数,需要对原来的节点进行关闭,切记一定要sleep(1),这样有时间可以去关闭,如果没有他会在后台一直执行。
MainWindow::~MainWindow()
{
executor.stopCommand();
executor.executeCommand("pkill -f _node");
QThread::sleep(1);
rosWorker->stop();
rclcpp::shutdown(); // 确保在退出时关闭ROS 2上下文
delete ui;
}
点击打开节点:
点击关闭节点(1):
点击x号关闭(2):同时也会这样关闭这些信号(但是要记得加延时)