提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
TODO:写完再整理
本文先对rviz三维可视化平台做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章
提示:以下是本篇文章正文内容
rviz是三维可视化工具,强调把已有的数据可视化显示,用于直观的显示数据。(机器人模型、坐标、运动规划,导航、点云、图像、SLAM)。rviz提供了很多插件,这些插件可以显示图像、模型、路径等信息,但是前提都是这些数据已经以话题、参数的形式发布,rviz做的事情就是订阅这些数据,并完成可视化的渲染,rviz帮助开发者实现所有可监测信息的图形化显示,开发者也可以在rviz的控制界面下,通过rviz的planning插件的功能,根据按钮、滑动条、数值等方式,控制机器人的行为【防盗标记–盒子君hzj】
.
.
rviz已经集成在桌面完整版的ROS系统当中,如果已经成功安装了桌面完整版的ROS,【防盗标记–盒子君hzj】可以直接跳过这一步骤,否则,请使用如下命令进行安装:
$ sudo apt-get install ros-kinetic-rviz
$ roscore
$ rosrun rviz rviz
.
.
0 :3D视图区,用于可视化显示数据,目前没有任何数据,所以显示黑色
1 :工具栏,提供视角控制、目标设置、发布地点等工具
2 :显示项列表,用于显示当前选择的显示插件,可以配置每个插件的属性
3 :视角设置区,可以选择多种观测视角
4 :时间显示区,显示当前的系统时间和ROS时间
.
.
进行数据可视化的前提当然是要有数据,假设需要可视化的数据以对应的消息类型发布,我们在rviz中使用相应的插件订阅该消息即可实现显示
可视化显示都是通过消息的订阅来完成的,机器人通过ROS发布数据,rviz订阅消息接收数据,然后显示【防盗标记–盒子君hzj】
(1)根据消息类型visualization_msgs编程
具体怎么操作可以去wiki上面查,掌握编程的套路,(编程根据数据接口和api会填入数据就行)
【常用的可视化类型记住一下,不常用的上wiki查】
(2)在程序上编程实现了可视化api后,在rviz界面上才可以进行勾选订阅和改变参数的
【DisplayTypes】显示元素的类型
写在了另外一个博客~
(1)Add添加显示插件
方法一:通过显示的类型添加(By display type)
方法二:通过话题添加(By topic)【常用 】,它会自动识别系统中发布的话题【防盗标记–盒子君hzj】
方法三:知道话题名字,输入进行搜索
(2)配置话题显示插件
添加完成后,rviz左侧的Dispaly中会列出已经添加的显示插件;点击插件列表前的加号,
可以打开一个属性列表(这些属性列表设置也是编程里面visualizatian_msgs接口的成员)
【可视化颜色、大小等等可以再程序里面改也可以再display中进行配置】,根据需求设置属性。
一般情况下,“Topic”属性较为重要,用来声明该显示插件所订阅的数据来源,如果订阅成功,在中间的显示区应该会出现可视化后的数据。
方法:File->Save Config As,下次直接打开这个.rviz就可以直接显示了
如果显示有问题,请检查属性区域的“Status”状态。Status有四种状态:OK、Warning、Error和Disabled,【防盗标记–盒子君hzj】
如果显示的状态不是OK,那么请查看错误信息,并详细检查数据发布是否正常
编程时要清除ros可视化,每次显示才会正常
【用好默认插件已经可以了-我们通常用的就是默认插件的数据类型】
(1)订阅话题的插件
包含坐标轴、摄像头图像、地图、激光等数据。
(2)发布话题的插件
(1)rviz中的Nav_3D的目标点(goal),在程序中是怎么被接收的?
【在目标点订阅函数的话题名中被订阅,在回调函数中对这个话题的目标值变量进行处理】
【话题名可以使用rviz中默认的话题名,也可以自己写一个rviz的插件自己进行定义,rviz和gazebo都是支持插件的】
(2)rviz最上面的界面栏可以发布话题,通过“+”“-”符号添加或者删除话题,【防盗标记–盒子君hzj】
发布的话题名字可以通过rostopic list查看,若是要修改可以在.rviz中进行修改
ROS的rviz仿真插件的编写,可以按照rviz提供的topic type的规范来写
作为一个平台,rviz可以显示的数据不仅仅如此。rviz支持插件扩展机制。如果需要添加其他数据的显示,也可以通过编写扩展插件的形式进行添加
jsk的rviz插件查一查怎么用?
planning有什么插件?【防盗标记–盒子君hzj】
可视化界面编程–QT(Qt中的重要概念——信号、槽,类似于回调函数)
整体原理
读取用户的输入,然后转换成ROS的topic
举例
//rviz_plugin_for_command.cpp
namespace test_rviz_plugin{
TestRvizPlugin::TestRvizPlugin(QWidget *parent):Panel(parent)
{
//创建按钮
auto *button_layout = new QHBoxLayout;
boundary_record_button_ = new QPushButton("Record Boundary",this);
button_layout->addWidget(boundary_record_button_);
connected_record_button_ = new QPushButton("Record Connected Road",this);
button_layout->addWidget(connected_record_button_);
virtualwall_record_button_ = new QPushButton("Record Virtual Wall",this);
button_layout->addWidget(virtualwall_record_button_);
start_task_button_ = new QPushButton("Start Task", this);
button_layout->addWidget(start_task_button_);
start_unFinish_task_button_ =
new QPushButton("Start unFinish Task", this);
button_layout->addWidget(start_unFinish_task_button_);
start_go_charge_button_ = new QPushButton("Start go charge Task", this);
button_layout->addWidget(start_go_charge_button_);
pause_task_button_ = new QPushButton("Pause Task", this);
button_layout->addWidget(pause_task_button_);
resume_task_button_ = new QPushButton("resume Task", this);
button_layout->addWidget(resume_task_button_);
leave_dock = new QPushButton("leave dock", this);
button_layout->addWidget(leave_dock);
end_task_button_ = new QPushButton("end Task", this);
button_layout->addWidget(end_task_button_);
setLayout(button_layout);
// 信号连接---点击信号发生,连接到槽函数callback
connect(boundary_record_button_, SIGNAL(clicked()), this,
SLOT(boundaryRecordCallback()));
connect(connected_record_button_, SIGNAL(clicked()), this,
SLOT(connectedRoadRecordCallback()));
connect(virtualwall_record_button_, SIGNAL(clicked()), this,
SLOT(virtualwallRcordCallback()));
connect(start_task_button_, SIGNAL(clicked()), this,
SLOT(startTaskCallback()));
connect(start_unFinish_task_button_, SIGNAL(clicked()), this,
SLOT(startUnFinishTaskCallback()));
connect(start_go_charge_button_, SIGNAL(clicked()), this,
SLOT(startGoChargeCallback()));
connect(pause_task_button_, SIGNAL(clicked()), this,
SLOT(PauseTaskCallback()));
connect(resume_task_button_, SIGNAL(clicked()), this,
SLOT(ResumeTaskCallback()));
connect(leave_dock, SIGNAL(clicked()), this, SLOT(LeaveDockCallback()));
connect(end_task_button_, SIGNAL(clicked()), this,
SLOT(EndTaskCallback()));
boundary_record_pub =
nh_.advertise<std_msgs::Bool>("record_boundary_button", 10);
connected_road_record_pub =
nh_.advertise<std_msgs::Bool>("record_connected_road_button", 10);
virtualwall_record_pub =
nh_.advertise<std_msgs::Bool>("record_virtualwall_button", 10);
start_task_pub = nh_.advertise<std_msgs::Bool>("start_task_button", 10);
start_unFinish_task_pub =
nh_.advertise<std_msgs::Bool>("start_unFinish_task_button_", 10);
start_go_charge_pub =
nh_.advertise<std_msgs::Bool>("start_go_charge_button_", 10);
pause_task_pub =
nh_.advertise<std_msgs::Bool>("pause_task_button_", 10);
resume_task_pub =
nh_.advertise<std_msgs::Bool>("resume_task_button_", 10);
leave_dock_pub =
nh_.advertise<std_msgs::Bool>("leave_dock_button_", 10);
end_task_pub = nh_.advertise<std_msgs::Bool>("end_task_button_", 10);
is_start_record_boundary.data = false;
is_start_record_connected_road.data = false;
is_start_record_virtualwall.data = false;
// start_task.data = false;
// start_unFinish_task.data = false;
// start_go_charge.data = false;
// pause_task.data = false;
// resume_task.data = false;
// leave_dock_task.data = false;
// end_task.data = false;
}
// 加载配置数据---必须要有的
void TestRvizPlugin::load(const rviz::Config &config) {
Panel::load(config);
}
// 将所有配置数据保存到给定的Config对象中。在这里,重要的是要对父类调用save(),以便保存类id和面板名称。---必须要有的
void TestRvizPlugin::save(rviz::Config config) const {
Panel::save(config);
}
void TestRvizPlugin::boundaryRecordCallback() {
if (!is_start_record_boundary.data)
is_start_record_boundary.data = true;
else
is_start_record_boundary.data = false;
ROS_INFO("Record Boundary");
boundary_record_pub.publish(is_start_record_boundary);
}
}
//rviz_plugin_for_command.h
#ifndef RVIZ_PLUGIN_FOR_COMMAND_H
#define RVIZ_PLUGIN_FOR_COMMAND_H
#include <ros/ros.h>
#include <rviz/panel.h> //plugin基类的头文件
#include <std_msgs/Bool.h>
#include <QHBoxLayout>
#include <QPushButton>
#include <QString>
namespace test_rviz_plugin{
class TestRvizPlugin : public rviz::Panel {
// 所有的plugin都必须是rviz::Panel的子类
// 后边需要用到Qt的信号和槽,都是QObject的子类,所以需要声明Q_OBJECT宏
Q_OBJECT
public:
// 构造函数,在类中会用到QWidget的实例来实现GUI界面,这里先初始化为0即可
TestRvizPlugin(QWidget *parent = 0);
// 重载rviz::Panel基类中的函数,用于保存、加载配置文件中的数据
virtual void load(const rviz::Config &config);
virtual void save(rviz::Config config) const;
// 回调函数
protected Q_SLOTS:
void boundaryRecordCallback();
void connectedRoadRecordCallback();
void virtualwallRcordCallback();
void startTaskCallback();
void startUnFinishTaskCallback();
void startGoChargeCallback();
void PauseTaskCallback();
void ResumeTaskCallback();
void LeaveDockCallback();
void EndTaskCallback();
private:
ros::NodeHandle nh_;
QPushButton *boundary_record_button_;
QPushButton *connected_record_button_;
QPushButton *virtualwall_record_button_;
QPushButton *start_task_button_;
QPushButton *start_unFinish_task_button_;
QPushButton *start_go_charge_button_;
QPushButton *pause_task_button_;
QPushButton *resume_task_button_;
QPushButton *leave_dock;
QPushButton *end_task_button_;
std_msgs::Bool is_start_record_boundary;
std_msgs::Bool is_start_record_connected_road;
std_msgs::Bool is_start_record_virtualwall;
// std_msgs::Bool start_task;
// std_msgs::Bool start_unFinish_task;
// std_msgs::Bool start_go_charge;
// std_msgs::Bool pause_task;
// std_msgs::Bool resume_task;
// std_msgs::Bool leave_dock_task;
// std_msgs::Bool end_task;
ros::Publisher boundary_record_pub;
ros::Publisher connected_road_record_pub;
ros::Publisher virtualwall_record_pub;
ros::Publisher start_task_pub;
ros::Publisher start_unFinish_task_pub;
ros::Publisher start_go_charge_pub;
ros::Publisher pause_task_pub;
ros::Publisher resume_task_pub;
ros::Publisher leave_dock_pub;
ros::Publisher end_task_pub;
};
}
#endif
效果如下
参考链接
https://zhuanlan.zhihu.com/p/39390512
启动rviz之前要先启动roscore,遇到错误或者警告先看提示
文章浏览阅读455次。我们日常开发中,经常会碰到并发的场景,在 Java 中语言体系里,我们会想到 ReentrantLock、CountDownLatch、Semaphore 等工具,但你是否清楚它们内部的实现原理?这些工具都很类似,底层都是基于AbstractQueuedSynchronizer(AQS)来实现的。今天我们就来一起学习 AQS 内部原理。_aqs 入对之后就一定顺序执行
文章浏览阅读4.1k次。编写一个程序,在屏幕上显示如下图形(图形的层数有输入者自行定义)源代码如下所示:#include<stdio.h>int main(){ int line; // 菱形总行数 int column; // 菱形总列数 int i; // 当前行 int j; // 当前列 printf("请输入菱形的行数(奇数):"); scanf("%d", &line); while(2) { if(line%_c语言画菱形
文章浏览阅读760次,点赞10次,收藏28次。/ 封装axios请求的模块// 用axios重新生成了请求的实例baseURL: ‘’, // 项目发送axios请求的公共地址 值为空timeout: 5000 // 请求超时时间 这里是请求超过五秒后还没有获得请求结果 提示请求超时})// config包含了请求相关的所有信息// 可以同过config对象给请求配置或者修改信息return config // 将配置完成的token返回 如果不返回 请求不会继续进行// 请求发生错误时的回调函数。
文章浏览阅读108次。最近在用c#写VSTO文字操作插件,因为要同时在WPS中有效,因此进行了提取。_wps officeimageid
文章浏览阅读4k次,点赞11次,收藏14次。 解决 Kali Linux中 Wine下英文方块乱码的方法 最近在wine下使用notepad++和sublime编辑器的时候,发现英文全部均是方块乱码,熟悉wine的朋友们一定会记得,在.wine文件夹下模拟器的文件结构是和Windows是非常相似的: nautilus .wine/drive_c/ wine中文件结构: Windows下文件结构: 如果,我们分别在Linux终端下对.wine/drive_c和在Powershell终端对C盘进行tree查看,就会发现文件是高_kali wine 中文方块
文章浏览阅读285次。vue3双向绑定原理
文章浏览阅读335次。3.1 可变字符串和BigDecimal3.1.1 可变字符串由于我们的字符串,是不可变的。每次都要在 字符串池里 新建和共享。这样效率很低,而且占用空间大。我们的 Java 就开发了 可变字符串下面的两者,都比传统的 String 类型要快很多。① StringBuffer:JDK1.0提供的,效率低,线程安全。它是实现开辟一个缓冲区,然后操作的时候,直接在 缓冲区里进行操作。② StringBuilder:JDK5.0提供的,效率高,线程不安全3.1.2 StringBuffer①_java字符串和bigdecimal类
文章浏览阅读2.6k次,点赞9次,收藏2次。32×32位寄存器堆设计,杭电计算机组成原理实验四,实验的开发工具是vivado2018,vivado2022也兼容2018_fpga2s需要多少位寄存器
文章浏览阅读644次。因为STM32F103C8T6,没有硬件随机数,所以需要自己通过软件配置。最后得到的mmm1变量为一个0~100的随机值。首先增加一段include用于产生真随机数。_stm32f103c8t6可以生成随机数么
文章浏览阅读4.5k次。先说下需求吧,我们的需求是通过一个经纬度,然后打开微信内置的地图页面代码如下://导航到停车场 gogogo() { //获取当前页面url //var url = location.href.split('#')[0]; // alert(11111) // window.location.href = // "https://apis.map.qq.com/uri/v1/marker?marker=coord:30.595810_h5 wx.openlocation
文章浏览阅读616次。一、DNS正向解析(权威配置) 作用: 用于域名到IP地址的映射,当DNS客户端请求解析某个域名时,DNS服务器通过正向查找,并返回给DNS客户端对应的IP地址 正向解析的部署:删除forwarders { 114.114.114.114; };(或者注释掉)vim /etc/named.rfc1912.zones新添加一个zone(可以复制19到23行,粘贴在底下再进行修改..._用于减少域名到ip地址的映射的技术是
文章浏览阅读2.2k次。学习利用Struts2,Spring,Hibernate这三个框架来搭建项目的主体架构,实现简单的一个登录注册功能_intellij idea ssh 登录系统教程