RVIZ和QT联合

RVIZ嵌入QT界面

引言:我们最近有一个解决的问题就是通过QT怎么来实时化来展示我们的机器人可视化界面,让我们的机器人实时运动来随着实时展现,我想到一种办法就是将我们的RVIZ来嵌入到我们的QT界面中来,让我们实现我们从ROS2来对我们的MIcro_ROS机器人的控制,并实时显示机器人的状态

最近在更新这个文件

第一天完成了MainWindow窗口,明天完成rviz_panel,主要实现我们MainWindow窗口的实现

如果你装载了ROS2 Humble Desktop 的话,RVIZ2应该是内置的

为了防止我们的安装是被我们安装下面的依赖,如果安装的是上面(ROS2 Humble Desktop ) 可以跳过这一步

bash 复制代码
sudo apt install ros-${ROS_DISTRO}-rviz2 \
                 ros-${ROS_DISTRO}-rviz-common \
                 ros-${ROS_DISTRO}-rviz-rendering \
                 ros-${ROS_DISTRO}-rviz-default-plugins \
                 ros-${ROS_DISTRO}-robot-state-publisher \
                 qtbase5-dev

QT基本知识

这里可能比较枯燥,如果使用QT Creator可能比这个要简单一点

这里适合有cpp基础的人来看,主要是写这个QT框架的使用

下面讲的是这个主程序都需要什么?

这可能和我的开发习惯有关,总是先对这个大的东西来进行我们的设计

下面个人因为也是第一次开发所以就尽可能写的注释详细,大家也可以根据我的思路来设计文件

头文件依赖

cpp 复制代码
// 类的声明
// QT 提供给我们的主窗口,我们要继承这个类的一些实现
class MainWindow : public QMainWindow
{
	 // Q_OBJECT 宏是 Qt 的元对象系统的一部分,用于支持信号和槽机制,在每一个QT程序之前都要加上
    Q_OBJECT
public:
	// Qwidget所有QT UI组件的基类,下面的UI组件都需要继承这个parent的对象
	explicit MainWindow(rclcpp::Node::share_ptr node,Qwidget*parent=nullptr);
	~MainWindow()override;
	// rviz初始化
	initalizeRviz();
// Qt 提供的槽函数,下面是槽函数的声明,就是我们在ROS中的subscription接受者,只是在单个线程中的这个函数
public slot:
	// 归零函数
	void OnGoHome();
	// 调节各个关节的角度的函数,需要传入各个关节的位置,所以要传入应该数组
	 void onJointAnglesChanged(const std::vector<double>& angles_deg);
	 // 准备位函数-预留给我们的抓取,visual pose,也可以使用检测我们的通信是否正常
	 void OnGoReady();
// 下面是不希望让用户修改的功能,相当于我们的config,个人的设计思路,一般把config文件放在我们的private下面
private:
	// 设计我们的界面的API函数
	void SetUpUi();
	// 发布我们的关节的状态
	void publishJointStates();
	// 下面是我们需要用到的成员函数,ROS编程中都有介绍我就不介绍了
	rclcpp::node::share_ptr node_;
	rclcpp::publisher<sensor_msgs::msgs::msgs::Jointstate>share_ptr Joint_pub_;
	rclcpp::TimerBase::SharedPtr timer_;
	    // UI 组件
    // rviz面板
    RvizPanel* rviz_panel_;
    // 关节控制面板
    JointControlPanel* joint_panel_;
    
    // 当前关节角度(弧度)
    std::vector<double> current_joint_angles_;
    // 关节名称 
    std::vector<std::string> joint_names_;
};

下面我们就对我们的功能来进行实现了

cpp 复制代码
// 构造函数,初始化成员
mainwindow::mainwindow(rclcpp::Node::Share_ptr node,QWidget *parent):
QMainWindow(parent)
,node_(node)
,rviz_panel(nullptr)
,joint_panel(nullptr)
{
	  // 初始化关节数据
    joint_names_ = {"joint_1", "joint_2", "joint_3", "joint_4", "joint_5", "joint_6"};
    current_joint_angles_.resize(6, 0.0);
    // 发布关节的位置
    Joint_pub_->node->create_publisher<sensor_msgs::msgs::msgs::JointState>("joint_states",10);
    // 定时发布关节位置,每50ms调用一次这个发布者
    timer_ =node->create_wall_timer(
    std::chrono::milliseconds(50),
    std::bind(&MainWindow::publishJointState,this));
    setUpUi();
}
// 析构函数的实现
~MainWindow(){
// 将定时器对象释放
if(timer_){
	timer_->cancel();
}
}
// 回调函数的实现
void MainWindow::publisherJointState(){
	// 初始化消息类型
	auto msg =sensor_msgs::msg::JointState();
	// 消息的头部分
	msg.header.stamp=node->get_clock()->now();
	// 消息的名称,我们开始初始化消息
	msg.name=joint_names_;
	msg.position=current_joint_angles_;
	// 通过Joint_pub_将消息发出来
	joint_pub_->publish(msg);
}
// 初始化U组件
void MainWindow::setUpUi(){
//可视化界面
 // 中心部件
 QWidget* central = new QWidget(this);
 setCentralWidget(central);
 QHBoxLayout * main_layout=new(central):
 // 创建面板
 QSplitter * splitter =new QSplitter(Qt::Horizontal,central);
 // 左侧RVIZ控制面板,先添加的是在左边,后添加的是在右边
 rviz_panel_ =new RvizPanel(node_,sqlitter);
 rviz_panel_->setMinmumWidth(600);
 // 将这个
 splitter->addWidget(rviz_panel_);
 // 右侧控制面板
 joint_panel_ = new JointControlPanel(6,Splitter);
 joint_panel_ ->setMinimumWidth(250);
 joint_panel_->setMaximumWidth(400);
 // 设置关节限位
 std::vector<std::pair<double,double>>limits={
        {-180, 180},  // Joint 1: 底座
        {-90, 90},    // Joint 2: 肩部
        {-135, 135},  // Joint 3: 肘部
        {-180, 180},  // Joint 4: 腕部旋转
        {-90, 90},    // Joint 5: 腕部俯仰
        {-180, 180}   // Joint 6: 末端
}
// 设置关节的限位
for (int i = 0; i < 6; i++) {
        joint_panel_->setJointName(i, joint_names_[i]);
        joint_panel_->setJointLimits(i, limits[i].first, limits[i].second);
}
splitter->setStretchFactor(0,3);
splitter->setStretchFactor(1,1);
main_layout->addWidget(splitter);
// 控制部分
	//连接信号
	connect(joint_panel_,&JointControlPanel::jointAngleschanged,
	this,&MainWindow::onJointAngleschanged);
	// 回零点信号和槽进行处理
	connect(joint_panel_,&JointControlPanel::jointAngleschanged,this,&MainWindow::onGoHome);
// 准备信号合槽进行处理
connect(joint_panel_,&JointControlPanel::jointAngleschanged,this,&MainWindow::onGoHome);
	
	
// 菜单部分,提供退出按键
QMenu * file_menu =menuBar()->addMenu("文件");
QAction * quit_action =file_menu=file_menu->addAction(
// 将菜单上的退出槽给我们的退出信号关联
connect(quit_action,&QAtion::triggered,this,&QMainWindow::close);
statusBar()->showMessage("就绪 - 等待 robot_description");

}
// 连接信号的处理函数
// 改变关节角度的信号函数
void MainWindow::JointAngleschanged(const std::vector<double>&angles_deg){
  // 度转弧度
    for (size_t i = 0; i < angles_deg.size() && i < current_joint_angles_.size(); i++) {
        current_joint_angles_[i] = angles_deg[i] * M_PI / 180.0;
    }
   // 更新状态栏
   QString status ="关节角度";
   for(size_t i=0;i<angles_deg.size();i++){
   // 这里类似于printf %1 表示第一个参数的值arg(i+1),%2表示第二个参数的值(angles_deg[i])
   //.arg(value, fieldWidth, format, precision)
   // fieldWidth = 0 最小宽度 0 不填充
   // format = 'f'  定点小数点格式
   // precision = 1 小数点后1位
	status+=QString"J%1=%2° "。arg(i+1).arg(angles_deg[i],0,'f',1);
}
}
// 回零位的函数
void MainWindow::onGoHome(){
	std::vector<double> home(6,0.0);
	joint_panel->setJointAngles(home);
}
// 回准备位的函数
void MainWindow::onGoReady(){
	std::vector<double> ready = {0, -45, 90, 0, 45, 0};
    joint_panel_->setJointAngles(ready);
}
void MainWindow::initalizeRviz(){
if(rviz_panel_){
	rviz_panel_->initalize();
	// 添加RVIZ可视化组件
	
	rviz_panel_->addGrid();
	// 机器人模型
	rviz_panel_->addRobotModel();
	// TF树
	rviz_panel_->addTF();
	statusBar()->showMessage("Rviz init success");
}
}
相关推荐
Lhan.zzZ8 小时前
基于Qt的UDP广播发现与TCP连接系统的设计与实现
qt·tcp/ip·udp
leiming69 小时前
c++ QT 开发第二天,用ui按钮点亮实体led
开发语言·qt·ui
hqwest10 小时前
码上通QT实战04--主窗体布局
开发语言·css·qt·布局·widget·layout·label
leiming610 小时前
c++ qt开发第一天 hello world
开发语言·c++·qt
赵民勇11 小时前
QML Base Type 详解
qt
hqwest12 小时前
码上通QT实战07--主窗体消息栏设计
开发语言·qt·qt事件·主窗体·stackedwidget
hqwest12 小时前
码上通QT实战06--导航按钮事件
开发语言·qt·mousepressevent·qfont·qpainter·qlineargradient·setbrush
CC.GG13 小时前
【Qt】常用控件----容器类控件(QGroupBox、QTabWidget )以及布局管理器
开发语言·qt
缘如风13 小时前
Qt Creator 断点调试断点停不住
开发语言·qt
hqwest13 小时前
码上通QT实战05--绘制导航按钮
开发语言·css·qt·自定义控件·qframe·布局ui