VINS_MONO视觉导航算法【三】ROS基础知识介绍
文章目录
- 其他文章
- 说明
- ROS
- launch文件
- 基本概念
- 定义
- 用途
- 文件结构
- 根标签
- 常用标签
- \<node>
- \<param>
- \<rosparam>
- \<remap>
- \<include>
- \<arg>
- \<group>
- 示例
- 基本示例
- 嵌套示例
- 使用方法
- 启动 *.launch 文件
- 传递参数
- 总结
- ROS topic
- Topic 的基本概念
- Topic 的工作原理
- 常用命令
- 示例
- 总结
- ROS常用命令
- rosrun
- roslaunch
- rosbag
- 主要功能
- roscore
- rosnode
- rostopic
- rosservice
- rosparam
- rqt
- ros::spin()
- 功能
- 使用场景
- 示例
- 解释
- 替代方案
- 总结
- ros::NodeHandle
- 主要功能
- 示例代码
- 详细解释
- advertise
- 语法和参数
- 参数说明
- 示例
- subscribe
- 语法和参数
- 参数说明
- 示例代码
- 详细解释
- 成员函数作为回调函数
- 详细解释
- ros::Publisher
- 主要功能
- 创建和使用 ros::Publisher
- 示例代码
- 详细解释
- 其他常用方法
- RViz
- RViz 的主要特点
- RViz 的使用
- 启动 RViz
- 加载配置文件
- 示例中的 RViz 配置
- 总结
- EVO
- EVO的主要功能
- 使用EVO进行性能测试的基本步骤
- 示例命令
- 结论
其他文章
- VINS_MONO视觉导航算法【一】基础知识介绍
- VINS_MONO视觉导航算法【二】论文讲解+GPU实现调研
- VINS_MONO视觉导航算法【三】ROS基础知识介绍
- VINS_MONO视觉导航算法【四】VINS_Mono代码解释
说明
这是第三篇,这一部分介绍一些ROS的基础概念,方便后面理解VINS_Mono代码。
ROS
launch文件
*.launch 文件是 ROS(Robot Operating System)中的一种 XML 文件,用于描述和配置多个节点、参数以及其他 ROS 系统组件。通过 *.launch 文件,可以一次性启动多个节点,并设置各种参数,从而简化了复杂系统的启动和配置过程。下面是关于 *.launch 文件的详细解释:
基本概念
定义
.launch 文件: 是一种 XML 文件,用于描述和配置 ROS 系统中的节点、参数、话题、服务等。
roslaunch 命令: 用于启动 *.launch 文件中定义的节点和配置。
用途
启动多个节点: 可以同时启动多个节点,而不需要在多个终端中分别运行 rosrun 命令。
设置参数: 可以在启动文件中设置节点的参数,这些参数会被加载到参数服务器中。
命名空间管理: 可以使用命名空间来组织和隔离节点,避免命名冲突。
重映射话题和服务: 可以在启动文件中重映射话题和服务的名称,使节点之间的通信更加灵活。
嵌套启动文件: 可以在一个启动文件中包含其他启动文件,实现模块化配置。
文件结构
根标签
: 是 *.launch 文件的根标签,所有其他标签都必须包含在这个标签内。
常用标签
<node>
描述: 用于启动一个 ROS 节点。在 *.launch 文件中,节点通过 标签来定义。每个节点代表一个独立的 ROS 进程,可以执行特定的任务,如传感器数据处理、导航算法等。
常用属性:
pkg: 节点所在的功能包名称。
type: 节点的可执行文件名称。
name: 节点启动时的名称。
args: 传递给节点的命令行参数。
output: 控制节点的日志输出,可以是 “screen”(输出到终端)或 “log”(输出到日志文件)。
respawn: 如果节点退出,是否自动重启,可以是 “true” 或 “false”。
required: 如果节点退出,是否终止其他节点,可以是 “true” 或 “false”。
ns: 节点的命名空间。
示例:
<node pkg="my_robot" type="navigation_node" name="nav_node" output="screen"><param name="target_location" value="room_1" ><remap from="odom" to="base_odom" ></node>
- pkg=“my_robot”: 节点所在的包名是 my_robot。
- type=“navigation_node”: 节点的可执行文件名是 navigation_node。
- name=“nav_node”: 节点启动时的名称是 nav_node。
- output=“screen”: 节点的日志输出到终端。
- : 设置节点的参数。
- : 重映射话题。
<param>
描述: 用于设置参数服务器中的参数。
常用属性:
name: 参数的名称。
value: 参数的值。
type: 参数的类型,可以是 “string”, “int”, “float”, “bool”, “yaml” 等。
textfile: 从文本文件中读取参数值。
binfile: 从二进制文件中读取参数值。
command: 通过命令行命令生成参数值。
<rosparam>
描述: 用于从 YAML 文件中加载参数到参数服务器,或从参数服务器中删除参数。
常用属性:
command: 操作类型,可以是 “load”, “dump”, “delete”。
file: YAML 文件的路径。
param: 参数的名称。
<remap>
描述: 用于重映射话题或服务的名称。
常用属性:
from: 原始名称。
to: 新名称。
<include>
描述: 用于包含其他启动文件。
常用属性:
file: 要包含的启动文件的路径。
<arg>
描述: 用于定义启动文件中的变量。
常用属性:
name: 变量的名称。
default: 变量的默认值。
value: 变量的值(如果在命令行中指定了值,则使用命令行中的值)。
<group>
描述: 用于将一组节点或配置项组织在一起。
常用属性:
ns: 组的命名空间。
if: 条件属性,如果条件为真,则执行组内的内容。
unless: 条件属性,如果条件为假,则执行组内的内容。
示例
基本示例
<launch><!-- 定义一个参数 --><param name="max_velocity" value="1.0" ><!-- 启动一个节点 --><node pkg="my_robot" type="navigation_node" name="nav_node" output="screen"><!-- 设置节点的参数 --><param name="target_location" value="room_1" ><!-- 重映射话题 --><remap from="odom" to="base_odom" ></node><!-- 包含另一个启动文件 --><include file="$(find my_robot)/launch/sensors.launch" ></launch>
嵌套示例
<launch><!-- 定义一个变量 --><arg name="use_sim_time" default="false" ><!-- 使用变量 --><param name="use_sim_time" value="$(arg use_sim_time)" ><!-- 启动一个节点组 --><group ns="robot1"><node pkg="my\_robot" type="navigation\_node" name="nav\_node" output="screen"><param name="target\_location" value="room\_1" /></node><node pkg="my_robot" type="navigation_node" name="nav_node" output="screen"><param name="target_location" value="room_1" ></node></group><!-- 启动另一个节点组 --><group ns="robot2" if="$(arg use_sim_time)"><node pkg="my\_robot" type="navigation\_node" name="nav\_node" output="screen"><param name="target\_location" value="room\_2" /></node></group></launch>
使用方法
启动 *.launch 文件
roslaunch package_name launch_file_name.launchpackage_name: 节点所在的功能包名称。launch_file_name: 启动文件的名称。
传递参数
roslaunch package_name launch_file_name.launch arg_name:=arg_valuearg_name: 变量的名称。arg_value: 变量的值。
总结
*.launch 文件是 ROS 中非常重要的配置文件,通过它可以方便地管理和启动多个节点,设置参数,重映射话题和服务,以及嵌套其他启动文件。合理使用 *.launch 文件可以大大提高开发效率,简化复杂系统的启动和配置过程。
ROS topic
在 ROS(Robot Operating System)中,topic 是一种通信机制,用于节点之间的异步消息传递。通过 topic,多个节点可以发布和订阅消息,实现数据的共享和交换。以下是关于 ROS topic 的详细解释:
Topic 的基本概念
发布者(Publisher):
发布者是向特定 topic 发送消息的节点。一个发布者可以向多个 topic 发布消息,但通常情况下,一个发布者只向一个 topic 发布消息。
订阅者(Subscriber):
订阅者是从特定 topic 接收消息的节点。一个订阅者可以订阅多个 topic,但通常情况下,一个订阅者只订阅一个 topic。
消息(Message):
消息是发布者和订阅者之间传递的数据单元。每条消息都有一个特定的数据结构,这个结构由 ROS 消息类型(如 std_msgs/String、sensor_msgs/Image 等)定义。
Topic 的工作原理
注册:
当一个节点启动时,它会向 ROS 主节点(master node)注册自己作为一个发布者或订阅者,并指定它所发布的或订阅的 topic。
发现:
主节点维护一个所有注册节点的列表,并将这些信息提供给其他节点。当一个订阅者节点启动时,它会向主节点查询是否有发布者节点正在发布它感兴趣的 topic。如果有,主节点会将发布者的地址提供给订阅者。
连接:
订阅者节点会直接与发布者节点建立 TCP/IP 连接,以便接收消息。这种连接是点对点的,不经过主节点。
消息传递:
发布者节点通过已建立的连接将消息发送给订阅者节点。订阅者节点接收到消息后,可以对其进行处理。
常用命令
列出所有活动 topic: rostopic list查看某个 topic 的消息类型: rostopic type /topic_name查看某个 topic 的消息内容: rostopic echo /topic_name发布消息到某个 topic: rostopic pub /topic_name std_msgs/String "data: 'Hello, World!'"查看某个 topic 的频率: rostopic hz /topic_name
示例
假设有一个发布者节点发布 /chatter topic 的字符串消息,一个订阅者节点订阅该 topic 并打印接收到的消息。
发布者节点代码示例(Python)
#!/usr/bin/env pythonimport rospyfrom std_msgs.msg import Stringdef talker():pub = rospy.Publisher('chatter', String, queue_size=10)rospy.init_node('talker', anonymous=True)rate = rospy.Rate(1) # 1 Hzwhile not rospy.is_shutdown():hello_str = "Hello, World! %s" % rospy.get_time()rospy.loginfo(hello_str)pub.publish(hello_str)rate.sleep()pub = rospy.Publisher('chatter', String, queue_size=10)rospy.init_node('talker', anonymous=True)rate = rospy.Rate(1) # 1 Hzwhile not rospy.is_shutdown():hello_str = "Hello, World! %s" % rospy.get_time()rospy.loginfo(hello_str)pub.publish(hello_str)rate.sleep()if name == 'main':try:talker()except rospy.ROSInterruptException:pass
订阅者节点代码示例(Python)
#!/usr/bin/env pythonimport rospyfrom std_msgs.msg import Stringdef callback(data):rospy.loginfo(rospy.get_caller_id() + " I heard %s", data.data)def listener():rospy.init_node('listener', anonymous=True)rospy.Subscriber("chatter", String, callback)rospy.spin()if name == 'main':listener()
总结
ROS topic 是一种轻量级、高效的通信机制,适用于节点之间的异步消息传递。通过发布者和订阅者的模式,ROS topic 实现了数据的解耦和灵活的通信架构。熟悉如何使用 topic 和相关的命令行工具,可以帮助开发者更好地管理和调试 ROS 系统。
ROS常用命令
rosrun
描述: 用于启动单个 ROS 节点。
语法:
rosrun package_name executable_name
示例:
rosrun my_robot navigation_node
用途: 主要用于调试和测试单个节点。
roslaunch
描述: 用于启动 *.launch 文件,可以同时启动多个节点,并设置参数、重映射话题等。
语法:
roslaunch package_name launch_file_name.launch
示例:
roslaunch my_robot navigation.launch
用途: 适用于启动复杂的系统,包含多个节点和参数配置。
rosbag
rosbag 是 ROS(Robot Operating System)中一个非常重要的命令行工具,用于记录和回放ROS话题消息。它主要用于数据采集、测试和回放,帮助开发者收集和分析机器人在实际运行中的数据。
主要功能
记录数据:
使用 rosbag record 命令可以记录指定的话题数据到一个文件中。
例如,记录所有话题的数据:
rosbag record -a
记录特定话题的数据:
rosbag record /topic1 /topic2
回放数据:
使用 rosbag play 命令可以回放记录的数据文件。
例如,回放一个数据文件:
rosbag play my_bag_file.bag
控制回放速度:
rosbag play my_bag_file.bag --rate=0.5 # 慢速回放
查看数据:
使用 rosbag info 命令可以查看数据文件的信息,包括话题列表、消息数量等。
例如,查看数据文件的信息:
rosbag info my_bag_file.bag
转换数据:
使用 rosbag filter 命令可以过滤和转换数据文件。
例如,过滤出特定话题的消息:
rosbag filter input.bag output.bag “topic == ‘/topic1’”
roscore
启动 ROS 核心节点,它是所有 ROS 节点通信的基础。
例如:
roscore
rosnode
查看和管理 ROS 节点。
例如,列出所有活动节点:
rosnode list
查看节点的信息:
rosnode info /node_name
rostopic
查看和管理 ROS 话题。
例如,列出所有活动话题:
rostopic list
查看某个话题的消息类型:
rostopic type /topic_name
发布消息到某个话题:
rostopic pub /topic_name std_msgs/String “data: ‘Hello, World!’”
查看某个话题的消息内容:
rostopic echo /topic_name
rosservice
查看和管理 ROS 服务。
例如,列出所有活动服务:
rosservice list
查看某个服务的服务类型:
rosservice type /service_name
调用某个服务:
rosservice call /service_name “request_data”
rosparam
查看和管理 ROS 参数服务器上的参数。
例如,列出所有参数:
rosparam list
设置参数:
rosparam set /param_name value
获取参数:
rosparam get /param_name
rqt
ROS 的图形用户界面工具,提供了多种插件用于可视化和调试ROS系统。
例如,启动 rqt:
rqt
ros::spin()
ros::spin(); 是 ROS(Robot Operating System)中的一个函数调用,用于启动一个循环,不断处理接收到的消息和服务请求。这是 ROS 节点的标准入口点之一,通常用于节点的主循环中。以下是详细的解释:
功能
消息处理:
ros::spin(); 会进入一个无限循环,监听并处理订阅的 topic 上的消息。
它会调用所有已注册的回调函数,处理接收到的消息。
服务请求处理:
如果节点提供了服务(services),ros::spin(); 也会处理这些服务请求。
定时器处理:
如果节点中有定时器(timers),ros::spin(); 也会处理定时器的回调。
使用场景
单线程节点:对于大多数简单的 ROS 节点,使用 ros::spin(); 是最常见的方式。它会阻塞当前线程,直到节点被关闭。
多线程节点:如果需要更细粒度的控制,可以使用 ros::spinOnce(); 结合自定义的循环来实现多线程处理。
示例
以下是一个简单的 ROS 节点示例,展示了如何使用 ros::spin();:
#include <ros/ros.h>#include <sensor_msgs/Image.h>// 回调函数,处理接收到的图像消息void imageCallback(const sensor_msgs::ImageConstPtr& msg){// 处理图像消息ROS_INFO("Received an image with timestamp: %f", msg->header.stamp.toSec());}int main(int argc, char **argv){// 初始化 ROS 节点ros::init(argc, argv, "image_subscriber");// 创建节点句柄ros::NodeHandle nh;// 订阅图像话题ros::Subscriber sub = nh.subscribe("camera/image_raw", 1, imageCallback);// 进入消息处理循环ros::spin();return 0;}
解释
初始化节点:
ros::init(argc, argv, “image_subscriber”); 初始化 ROS 节点,节点名称为 image_subscriber。
创建节点句柄:
ros::NodeHandle nh; 创建一个节点句柄,用于订阅话题、发布消息等。
订阅话题:
ros::Subscriber sub = nh.subscribe(“camera/image_raw”, 1, imageCallback); 订阅名为 camera/image_raw 的话题,并指定回调函数 imageCallback 处理接收到的消息。
进入消息处理循环:
ros::spin(); 进入消息处理循环,监听并处理订阅的话题上的消息。
替代方案
ros::spinOnce();:
如果需要在主循环中进行其他操作,可以使用 ros::spinOnce(); 结合自定义的循环。
示例:
int main(int argc, char **argv)
{ros::init(argc, argv, "image_subscriber");ros::NodeHandle nh;ros::Subscriber sub = nh.subscribe("camera/image_raw", 1, imageCallback);while (ros::ok()){// 处理一次消息ros::spinOnce();// 其他操作// ...// 控制循环频率ros::Rate loop_rate(10); // 10 Hzloop_rate.sleep();}return 0;ros::init(argc, argv, "image_subscriber");ros::NodeHandle nh;ros::Subscriber sub = nh.subscribe("camera/image_raw", 1, imageCallback);while (ros::ok()){// 处理一次消息ros::spinOnce();// 其他操作// ...// 控制循环频率ros::Rate loop_rate(10); // 10 Hzloop_rate.sleep();}return 0;}
在这个示例中,ros::spinOnce(); 会在每次循环中处理一次消息,然后执行其他操作,最后通过 ros::Rate 控制循环频率。
总结
ros::spin(); 是一个简单而强大的函数,适用于大多数单线程的 ROS 节点。它会持续监听并处理消息,直到节点被关闭。如果你需要更细粒度的控制,可以考虑使用 ros::spinOnce(); 结合自定义的循环。
ros::NodeHandle
ros::NodeHandle 是ROS(Robot Operating System)中的一个核心类,用于管理ROS节点的各种资源,包括创建发布者(publishers)、订阅者(subscribers)、服务客户端和服务服务器等。ros::NodeHandle 对象是每个ROS节点的主要入口点,提供了许多方法来与ROS系统进行交互。
主要功能
创建发布者:
使用 advertise<>() 方法创建发布者,用于发布消息到特定的话题。
创建订阅者:
使用 subscribe<>() 方法创建订阅者,用于订阅特定话题的消息。
创建服务客户端:
使用 serviceClient<>() 方法创建服务客户端,用于调用服务。
创建服务服务器:
使用 advertiseService<>() 方法创建服务服务器,用于处理服务请求。
获取参数:
使用 getParam()、param() 等方法获取节点的参数。
设置参数:
使用 setParam() 方法设置节点的参数。
私有命名空间:
使用 ros::NodeHandle nh(“~”) 创建一个私有命名空间的节点句柄,用于访问节点私有的参数。
示例代码
以下是一个简单的示例,展示了如何使用 ros::NodeHandle 创建发布者和订阅者:
#include <ros/ros.h>#include <std_msgs/String.h>// 回调函数,处理接收到的消息void chatterCallback(const std_msgs::String::ConstPtr& msg){ROS_INFO("I heard: [%s]", msg->data.c_str());}int main(int argc, char **argv){// 初始化ROS节点ros::init(argc, argv, "example_node");// 创建ROS节点句柄ros::NodeHandle nh;// 创建发布者,发布字符串消息到 "chatter" 话题ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);// 创建订阅者,订阅 "chatter" 话题的字符串消息ros::Subscriber chatter_sub = nh.subscribe("chatter", 1000, chatterCallback);// 主循环ros::Rate loop_rate(10); // 10 Hzint count = 0;while (ros::ok()){// 创建消息std_msgs::String msg;std::stringstream ss;ss << "hello world " << count;msg.data = ss.str();// 发布消息chatter_pub.publish(msg);// 处理ROS回调函数ros::spinOnce();// 控制主循环的频率loop_rate.sleep();++count;}return 0;}
详细解释
初始化ROS节点:
ros::init(argc, argv, “example_node”);
初始化ROS节点,传递命令行参数和节点名称。
创建ROS节点句柄:
ros::NodeHandle nh;
创建一个 ros::NodeHandle 对象,用于管理节点的资源。
创建发布者:
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);
使用 advertise<>() 方法创建一个发布者,发布 std_msgs::String 类型的消息到 chatter 话题,队列大小为1000。
创建订阅者:
ros::Subscriber chatter_sub = nh.subscribe(“chatter”, 1000, chatterCallback);
使用 subscribe<>() 方法创建一个订阅者,订阅 chatter 话题的 std_msgs::String 类型的消息,并指定回调函数 chatterCallback。
回调函数:
void chatterCallback(const std_msgs::String::ConstPtr& msg){ROS_INFO("I heard: [%s]", msg->data.c_str());}
回调函数 chatterCallback 处理接收到的消息,并打印消息内容。
主循环:
ros::Rate loop_rate(10); // 10 Hzint count = 0;while (ros::ok()){// 创建消息std_msgs::String msg;std::stringstream ss;ss << "hello world " << count;msg.data = ss.str();// 发布消息chatter_pub.publish(msg);// 处理ROS回调函数ros::spinOnce();// 控制主循环的频率loop_rate.sleep();++count;}
主循环中创建消息并发布,处理ROS回调函数,并控制主循环的频率。
advertise
在ROS(Robot Operating System)中,n.advertise<>() 是一个用于创建发布者(publisher)的方法。这个方法属于 ros::NodeHandle 类,用于声明一个节点将要发布某个话题的消息。
语法和参数
ros::Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false);
参数说明
topic (const std::string& topic):
话题名称,用于标识发布的消息流。例如 “imu_propagate”。
queue_size (uint32_t queue_size):
消息队列的大小。这是发布者内部用于缓冲消息的队列大小。如果消息生成速度快于消息传输速度,队列可以暂时存储多余的消息,防止消息丢失。
latch (bool latch = false):
是否启用消息缓存(可选参数,默认为 false)。如果设置为 true,则最后一条消息会被缓存,新订阅者订阅时会立即收到这条消息,而不需要等待新的消息发布。
示例
ros::Publisher pub_latest_odometry = n.advertise<nav_msgs::Odometry>(“imu_propagate”, 1000);
详细解释
创建发布者对象:
n 是一个 ros::NodeHandle 对象,用于管理ROS节点的各种资源。
advertise<>() 方法创建一个发布者对象,并将其赋值给 pub_latest_odometry 变量。
指定消息类型:
<nav_msgs::Odometry> 是模板参数,指定了发布者将要发布的消息类型。在这个例子中,消息类型是 nav_msgs::Odometry。
指定话题名称:
“imu_propagate” 是话题名称,表示发布的消息将通过这个话题进行传输。
指定队列大小:
1000 是消息队列的大小,表示发布者内部可以缓存最多1000条消息。
subscribe
subscribe<>() 方法是ROS(Robot Operating System)中 ros::NodeHandle 类的一个重要方法,用于创建订阅者(subscriber)。通过订阅者,节点可以接收来自特定话题的消息。订阅者会在每次接收到新消息时调用指定的回调函数。
语法和参数
subscribe(const std::string& topic, uint32_t queue_size, void(*fp)(M const&), const boost::shared_ptr& obj, const TransportHints& transport_hints = TransportHints());
参数说明
topic (const std::string& topic):
话题名称,用于标识订阅的消息流。例如 “chatter”。
queue_size (uint32_t queue_size):
消息队列的大小。这是订阅者内部用于缓冲接收到的消息的队列大小。如果消息生成速度快于消息处理速度,队列可以暂时存储多余的消息,防止消息丢失。
fp (void(*fp)(M const&)):
回调函数指针,用于处理接收到的消息。M 是消息类型,例如 std_msgs::String。
obj (const boost::shared_ptr& obj):
回调函数所属的对象。如果你的回调函数是类的成员函数,需要传递一个指向该对象的智能指针。
transport_hints (const TransportHints& transport_hints = TransportHints()):
传输提示(可选参数),用于指定传输方式,例如TCP或UDP。默认情况下,使用系统的默认传输方式。
示例代码
以下是一个完整的示例,展示了如何使用 subscribe<>() 创建订阅者并处理接收到的消息。
示例代码
#include <ros/ros.h>#include <std_msgs/String.h>// 回调函数,处理接收到的消息void chatterCallback(const std_msgs::String::ConstPtr& msg){ROS_INFO("I heard: [%s]", msg->data.c_str());}int main(int argc, char **argv){// 初始化ROS节点ros::init(argc, argv, "example_subscriber");// 创建ROS节点句柄ros::NodeHandle nh;// 创建订阅者,订阅 "chatter" 话题的字符串消息ros::Subscriber sub = nh.subscribe("chatter", 1000, chatterCallback);// 主循环ros::spin();return 0;}
详细解释
初始化ROS节点:
ros::init(argc, argv, “example_subscriber”);
初始化ROS节点,传递命令行参数和节点名称。
创建ROS节点句柄:
ros::NodeHandle nh;
创建一个 ros::NodeHandle 对象,用于管理节点的资源。
创建订阅者:
ros::Subscriber sub = nh.subscribe(“chatter”, 1000, chatterCallback);
使用 subscribe<>() 方法创建一个订阅者,订阅 chatter 话题的 std_msgs::String 类型的消息,队列大小为1000,并指定回调函数 chatterCallback。
回调函数:
void chatterCallback(const std_msgs::String::ConstPtr& msg){ROS_INFO("I heard: [%s]", msg->data.c_str());}
回调函数 chatterCallback 处理接收到的消息,并打印消息内容。
主循环:
ros::spin();
使用 ros::spin() 进入主循环,处理所有的回调函数。ros::spin() 会一直阻塞,直到节点关闭。
成员函数作为回调函数
如果你的回调函数是类的成员函数,需要传递一个指向该对象的智能指针。以下是一个示例:
示例代码
#include <ros/ros.h>#include <std_msgs/String.h>class ExampleSubscriber{public:// 构造函数ExampleSubscriber(){// 创建ROS节点句柄ros::NodeHandle nh;// 创建订阅者,订阅 "chatter" 话题的字符串消息sub = nh.subscribe("chatter", 1000, &ExampleSubscriber::chatterCallback, this);}// 回调函数,处理接收到的消息void chatterCallback(const std_msgs::String::ConstPtr& msg){ROS_INFO("I heard: [%s]", msg->data.c_str());}private:ros::Subscriber sub;};int main(int argc, char **argv){// 初始化ROS节点ros::init(argc, argv, "example_subscriber");// 创建订阅者对象ExampleSubscriber example_subscriber;// 主循环ros::spin();return 0;}
详细解释
构造函数:
ExampleSubscriber(){// 创建ROS节点句柄ros::NodeHandle nh;// 创建订阅者,订阅 "chatter" 话题的字符串消息sub = nh.subscribe("chatter", 1000, &ExampleSubscriber::chatterCallback, this);}
在构造函数中创建ROS节点句柄,并使用 subscribe<>() 方法创建订阅者,订阅 chatter 话题的 std_msgs::String 类型的消息,队列大小为1000,并指定成员函数 chatterCallback 作为回调函数,同时传递 this 指针。
成员函数作为回调函数:
void chatterCallback(const std_msgs::String::ConstPtr& msg){ROS_INFO("I heard: [%s]", msg->data.c_str());}
成员函数 chatterCallback 处理接收到的消息,并打印消息内容。
ros::Publisher
ros::Publisher 是ROS(Robot Operating System)中的一个类,用于发布消息到特定的话题。通过 ros::Publisher,你可以将数据发送到其他节点订阅的同一个话题,从而实现节点之间的通信。
主要功能
发布消息:
使用 publish() 方法将消息发布到指定的话题。
检查是否有订阅者:
使用 getNumSubscribers() 方法检查当前话题是否有订阅者。
获取话题名称:
使用 getTopic() 方法获取当前发布者的话题名称。
创建和使用 ros::Publisher
创建发布者
创建发布者通常通过 ros::NodeHandle 的 advertise<>() 方法完成。以下是创建发布者的步骤:
初始化ROS节点:
ros::init(argc, argv, “example_node”);
创建ROS节点句柄:
ros::NodeHandle nh;
创建发布者:
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);
发布消息
使用 publish() 方法发布消息:
std_msgs::String msg;
msg.data = “Hello, World!”;
chatter_pub.publish(msg);
示例代码
以下是一个完整的示例,展示了如何创建和使用 ros::Publisher:
#include <ros/ros.h>#include <std_msgs/String.h>int main(int argc, char **argv){// 初始化ROS节点ros::init(argc, argv, "example_publisher");// 创建ROS节点句柄ros::NodeHandle nh;// 创建发布者,发布字符串消息到 "chatter" 话题ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);// 主循环ros::Rate loop_rate(10); // 10 Hzint count = 0;while (ros::ok()){// 创建消息std_msgs::String msg;std::stringstream ss;ss << "hello world " << count;msg.data = ss.str();// 发布消息chatter_pub.publish(msg);// 输出发布信息ROS_INFO("Published: %s", msg.data.c_str());// 处理ROS回调函数ros::spinOnce();// 控制主循环的频率loop_rate.sleep();++count;}return 0;}
详细解释
初始化ROS节点:
ros::init(argc, argv, “example_publisher”);
初始化ROS节点,传递命令行参数和节点名称。
创建ROS节点句柄:
ros::NodeHandle nh;
创建一个 ros::NodeHandle 对象,用于管理节点的资源。
创建发布者:
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);
使用 advertise<>() 方法创建一个发布者,发布 std_msgs::String 类型的消息到 chatter 话题,队列大小为1000。
主循环:
ros::Rate loop_rate(10); // 10 Hzint count = 0;while (ros::ok()){// 创建消息std_msgs::String msg;std::stringstream ss;ss << "hello world " << count;msg.data = ss.str();// 发布消息chatter_pub.publish(msg);// 输出发布信息ROS_INFO("Published: %s", msg.data.c_str());// 处理ROS回调函数ros::spinOnce();// 控制主循环的频率loop_rate.sleep();++count;}
在主循环中,创建消息并发布,输出发布信息,处理ROS回调函数,并控制主循环的频率。
其他常用方法
检查是否有订阅者:
int num_subscribers = chatter_pub.getNumSubscribers();if (num_subscribers > 0){ROS_INFO("There are %d subscribers to the topic.", num_subscribers);}
获取话题名称:
std::string topic_name = chatter_pub.getTopic();
ROS_INFO(“Topic name: %s”, topic_name.c_str());
RViz
RViz (Robot Visualization)是 Robot Operating System (ROS) 中的一款强大的三维可视化工具,广泛用于机器人系统的开发和调试。它可以帮助开发者以图形化的方式查看和分析机器人模型、传感器数据、环境地图等多种类型的数据。通过 RViz,用户可以直观地了解机器人的状态和行为,从而提高开发和调试的效率。
RViz 的主要特点
可视化机器人模型:
RViz 可以加载和显示机器人的三维模型,并根据实际的关节状态进行动态更新。这使得用户能够直观地了解机器人的外观和姿态。12
显示传感器数据:
RViz 可以接收和显示来自机器人传感器(如激光雷达、摄像头、IMU 等)的数据。用户可以实时查看和分析传感器数据,帮助理解机器人周围的环境。16
生成导航地图:
RViz 可以通过接收来自 SLAM(Simultaneous Localization and Mapping)或其他建图算法的数据,生成并显示机器人所在环境的二维或三维地图。16
调试运动规划:
RViz 可以显示机器人的路径规划结果,并提供交互式界面来调试和优化运动规划算法。用户可以可视化虚拟路径、障碍物和碰撞检测等信息。16
可定制性:
RViz 提供了丰富的配置选项,允许用户按照自己的需求自定义界面布局、可视化对象和颜色风格等。用户可以根据实际情况进行个性化设置,以满足特定的可视化需求。16
RViz 的使用
启动 RViz
RViz 可以通过命令行启动,通常需要先启动 ROS 核心节点 roscore,然后再启动 RViz。启动命令如下:
roscore
rosrun rviz rviz
加载配置文件
RViz 支持加载配置文件,这些文件包含了 RViz 的初始设置,如显示的内容、参数配置等。配置文件通常以 .rviz 为扩展名。在启动 RViz 时,可以通过 -d 参数指定配置文件的路径:
rosrun rviz rviz -d path/to/config_file.rviz
示例中的 RViz 配置
在你提供的 *.launch 文件中,RViz 的启动配置如下:
<launch><node name="rvizvisualisation" pkg="rviz" type="rviz" output="log" args="-d $(find vins_estimator)/../config/vins_rviz_config.rviz" ></launch>
解释
标签: 用于定义一个 ROS 节点。
name=“rvizvisualisation”: 节点的名称为 rvizvisualisation。
pkg=“rviz”: 节点所在的包名是 rviz。
type=“rviz”: 节点的可执行文件名是 rviz。
output=“log”: 节点的日志输出到日志文件。
args=“-d $(find vins_estimator)/…/config/vins_rviz_config.rviz”: 传递给节点的命令行参数,用于指定 RViz 配置文件的路径。
$(find vins_estimator): 这是一个 ROS 变量,用于查找 vins_estimator 包的路径。
/…/config/vins_rviz_config.rviz: 从 vins_estimator 包的路径向上一级目录,然后进入 config 文件夹,找到 vins_rviz_config.rviz 配置文件。
总结
RViz 是 ROS 中一个非常强大的三维可视化工具,通过它可以直观地查看和分析机器人系统的各种数据。通过 *.launch 文件,可以方便地启动 RViz 并加载预设的配置文件,从而快速进入开发和调试状态。在你的示例中,rvizvisualisation 节点启动时会加载 vins_rviz_config.rviz 配置文件,确保 RViz 以预设的显示效果启动。17
EVO
EVO(Evaluation of Visual Odometry / SLAM)是一款开源软件工具,专门用于评估视觉里程计(Visual Odometry, VO)和同时定位与地图构建(Simultaneous Localization and Mapping, SLAM)系统的性能。EVO 提供了一套完整的评估框架,包括数据处理、结果可视化以及多种评价指标计算等功能,适用于研究人员和工程师对不同VO/SLAM算法的性能进行比较和分析。
EVO的主要功能
多格式支持:
EVO 支持多种数据格式,包括但不限于 TUM RGB-D 数据集格式、Kitti 数据集格式、Euroc MAV 数据集格式等,这使得它能够兼容不同的实验数据源。
丰富的评价指标:
EVO 提供了多种评价指标,如绝对轨迹误差(ATE)、相对位姿误差(RPE)等,这些指标能够从不同角度评估VO/SLAM系统的精度和稳定性。
灵活的结果可视化:
EVO 具有强大的结果可视化功能,可以生成轨迹对比图、误差分布图等多种图表,帮助用户直观地理解评估结果。
命令行接口和Python API:
EVO 不仅提供了命令行工具,还提供了Python API,这使得用户可以在脚本中集成EVO的功能,实现自动化评估流程。
易于扩展:
EVO 的设计考虑到了灵活性和可扩展性,用户可以根据需要添加新的数据格式支持或自定义评估指标。
使用EVO进行性能测试的基本步骤
准备数据:
准备好待评估的VO/SLAM系统的输出数据,通常是估计的轨迹文件,格式应符合所选数据集的标准。
获取真值数据:
获取相应的真值数据(ground truth),这些数据通常来自高精度的测量设备或数据集本身提供的真值轨迹。
运行EVO工具:
使用EVO的命令行工具或Python API,将估计的轨迹与真值轨迹进行比较,计算评价指标。
分析结果:
分析EVO生成的报告和图表,评估VO/SLAM系统的性能。
示例命令
以下是一个使用EVO命令行工具评估ATE的例子:
evo_ape tum groundtruth.txt trajectory.txt --plot --plot_mode=xy --save_plot plot.png
evo_ape:调用EVO的绝对轨迹误差评估模块。
tum:指定数据格式为TUM格式。
groundtruth.txt:真值轨迹文件。
trajectory.txt:待评估系统输出的估计轨迹文件。
–plot:生成轨迹对比图。
–plot_mode=xy:指定绘制模式为二维平面图。
–save_plot plot.png:将生成的图表保存为图片文件。
结论
EVO 是一个功能全面且灵活的评估工具,对于从事VO/SLAM研究和开发的人员来说,是非常有用的工具。通过EVO,可以轻松地评估和比较不同算法的性能,从而推动相关技术的发展。
相关文章:
VINS_MONO视觉导航算法【三】ROS基础知识介绍
文章目录 其他文章说明ROSlaunch文件基本概念定义用途 文件结构根标签常用标签\<node>\<param>\<rosparam>\<remap>\<include>\<arg>\<group> 示例基本示例嵌套示例 使用方法启动 *.launch 文件传递参数 总结 ROS topicTopic 的基本…...
Python 3 教程第13篇(集合)
Python3 集合 集合(set)是一个无序的不重复元素序列。 集合中的元素不会重复,并且可以进行交集、并集、差集等常见的集合操作。 可以使用大括号 { } 创建集合,元素之间用逗号 , 分隔, 或者也可以使用 set() 函数创建集…...
cesium 3dtile ClippingPlanes 多边形挖洞ClippingPlaneCollection
原理就是3dtiles里面的属性clippingPlanes 采用ClippingPlaneCollection,构成多边形来挖洞。 其次就是xyz法向量挖洞 clippingPlanes: new this.ffCesium.Cesium.ClippingPlaneCollection({unionClippingRegions: true, // true 表示多个切割面能合并为一个有效的…...
开发者如何使用GCC提升开发效率GUI操作
看此篇前请先阅读https://blog.csdn.net/qq_20330595/article/details/144139026?spm1001.2014.3001.5502 先上效果图 找到对应的环境版本 配置环境 目录结构 CtrlShiftP c_cpp_properties.json {"configurations": [{"name": "Win32","i…...
什么是隐式类型转换?
隐式类型转换(Implicit Type Conversion)是指编译器在没有明确要求的情况下,自动地将一种类型的值转换为另一种类型。C 语言支持隐式类型转换,这通常发生在表达式运算或函数调用中,以确保操作数或参数的类型兼容性。 隐…...
爬虫专栏第三篇:Python 实战:运用 requests 突破京东商品评论获取难题(含 cookie 处理与编码设置技巧
简介:本文以京东商品评论为例,详细介绍了使用requests库获取网页数据的过程,包括从打开商品评价页面、抓包分析找到评论接口,到处理cookie参数、解决cookie字符串解析问题,以及设置正确的编码以成功获取评论数据&#…...
HCIE:详解OSPF,从基础到高级特性再到深入研究
目录 前言 一、OSPF协议基本原理 简介 基本原理 OSPF路由器类型 OSPF网络类型 OSPF报文类型和封装 OSPF邻居的建立的维护 DR和BDR的选举 伪节点 LSDB的更新 OSPF的配置 二、OSPF的高级特性 虚连接(Virtual-Link) OSPF的LSA和路由选择 OSPF…...
第六十六条:谨慎使用本地方法
其实在java中有很多都是采用C或C语言实现的比如native这种的本地方法,感兴趣的可以找源代码看看。 Java Native Interface (JNI) 允许Java应用程序可以调用本地方法(native method),所谓本地方法是指使用本地程序设计语言…...
neo4j如何存储关于liquidity structure的层次和关联结构
在 Neo4j 中存储关于流动性结构(liquidity structure)的层次和关联结构非常适合,因为 Neo4j 是一个基于图的数据库,能够自然地建模和存储复杂的关系和层次结构。下面是如何在 Neo4j 中设计和实现这样的数据模型的详细步骤和示例。…...
45 基于单片机的信号选择与温度变化
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机,采用DS18B20检测温度,通过三种LED灯代表不同状态。 采用DAC0832显示信号脉冲,通过8位数码管显示温度。 信号脉冲可以根据两个按键分别调整为正弦…...
2、Three.js初步认识场景Scene、相机Camera、渲染器Renderer三要素
三要素之间关系: 有了虚拟场景Scene,相机录像Camera,在相机小屏幕上看到的Renderer Scene当前空间 Mesh人在场景 Camera相机录像 Renderer显示器上 首先先描述下Scene: 这个场景为三要素之一,一切需要展示的东西都需…...
Java基础面试题06:hashCode()和equals()方法的重要性体现在什么地方?
前言 Java中的hashCode()和equals()方法看似是干同一件事,但它们的使用场景和作用却大有不同。为了让程序更高效、更准确地运行,我们需要对这两者有深入的了解,并掌握它们的重写规则。 hashCode()和equals()的基础知识 什么是hashCode&…...
Matlab Simulink HDL Coder开发流程(一)— 创建HDL兼容的Simulink模型
创建HDL兼容的Simulink模型 一、使用Balnk DUT模板二、从HDL Coder库中选择模块三、为DUT开发算法/功能四、为设计创建Testbench五、仿真验证设计功能六、Simulink模型生成HDL代码 这个例子说明了如何创建一个用于生成HDL代码的Simulink模型。要创建兼容HDL代码生成的MATLAB算法…...
C#基础之方法
文章目录 1 方法1.1 定义方法1.2 参数传递1.2.1 按值传递参数1.2.2 按引用传递参数1.2.3 按输出传递参数1.2.4 可变参数 params1.2.5 具名参数1.2.6 可选参数 1.3 匿名方法1.3.1 Lambda 表达式1.3.1.1 定义1.3.1.2 常用类型1.3.1.3 Lambda 表达式与 LINQ1.3.1.4 Lambda 表达式的…...
Pytest框架学习19--参数化2
1、数据源是yaml 安装yaml,使用safe_load方法读取文件,解析出数据 pip install PyYAML # test_data.yaml test_case_1:a: 2b: 3expected_result: 5test_case_2:a: -1b: 10expected_result: 9test_case_3:a: 0b: 0expected_result: 0# test_code.pyimpor…...
VSCode中“Run Code”运行程序时,终端出现中文乱码解决方法
问题描述 在VSCode中“Run Code”运行程序时,终端输出结果出现中文乱码现象: 解决方法 1. 检查系统cmd的默认编码 查看Windows终端当前编码方式的命令: chcp输出结果是一段数字代码,如936,这说明当前的cmd编码方式…...
HarmonyOS 5.0应用开发——列表(List)
【高心星出品】 文章目录 列表(List)列表介绍列表布局设置主轴方向设置交叉轴方向 列表填充分组列表填充 滚动条位置设置滚动位置滚到监听 列表项侧滑 列表(List) 列表介绍 列表作为一种容器,会自动按其滚动方向排列…...
C++设计模式外观模式(Facade)
什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,它的核心目的是解耦抽象部分和实现部分,让它们可以独立变化。在简单的描述中,桥接模式可以让你在不修改原有代码的情况下,分别…...
《Python基础》之Numpy库
目录 简介 一、创建数组 1、根据列表创建数组 2、创建全0数组 3、创建全1数组 4、创建单位矩阵 5、创建随机数数组 二、查看数组的属性 三、 数组的操作 1、索引和切片 2、变形 3、拼接 (1)、vstack() 纵向拼接 (2)、hs…...
Python异步编程与API调用:提高效率与性能的实践指南
引言 在当今的软件开发领域,异步编程已成为提高应用程序性能和响应能力的关键技术。特别是在处理I/O密集型任务,如API调用时,异步编程能够显著提升效率。本文将通过一个具体的示例——使用Python的asyncio和aiohttp库来异步调用APIÿ…...
Zookeeper3.4.14集群安装
Zookeeper3.4.14三节点集群安装 为保证集群高可用,Zookeeper 集群的节点数最好是奇数,最少有三个节点,所以这里搭建一个三个节点的集群。(在一个节点模拟三节点,真实的三节点把ip替换一下即可,按照hadoop案件把网络打…...
电机驱动MCU介绍
电机驱动MCU是一种专为电机控制设计的微控制器单元,它集成了先进的控制算法和高性能的功率输出能力。 电机驱动MCU采用高性能的处理器核心,具有快速的运算速度和丰富的外设接口。它内置了专业的电机控制算法,包括PID控制、FOC(Fi…...
Python 面向对象编程详解
Python 面向对象编程详解 面向对象编程(OOP)是一种编程范式,它使用“对象”来设计软件。在 Python 中,面向对象编程非常强大,允许开发者通过类(class)和对象(object)来模…...
网络技术-不使用K8S情况下哪些方法可搭建服务链编排
在不使用Kubernetes(k8s)(或不使用Docker环境技术的)情况下,搭建服务链编排环境需要依赖其他服务编排和容器化技术,或者采用传统的虚拟机和服务管理方法。以下是一些可能的解决方案: 一、使用其…...
JVM_总结详解
1、CPU和内存的交互 了解jvm内存模型前,了解下cpu和计算机内存的交互情况。【因为Java虚拟机内存模型定义的访问操作与计算机十分相似】 有篇很棒的文章,从cpu讲到内存模型:[什么是java内存模型?] 在计算机中,cpu和内存的交互最…...
go语言 Pool实现资源池管理数据库连接资源或其他常用需要共享的资源
go Pool Pool用于展示如何使用有缓冲的通道实现资源池,来管理可以在任意数量的goroutine之间共享及独立使用的资源。这种模式在需要共享一组静态资源的情况(如共享数据库连接或者内存缓冲区)下非 常有用。如果goroutine需要从池里得到这些资…...
初级数据结构——邻接表
目录 前言一、定义与结构二、特点与性质三、构建方式四、操作与应用五、代码模版六、经典例题[1.——LCP 07. 传递信息](https://leetcode.cn/problems/chuan-di-xin-xi/description/)代码题解 [2.——547. 省份数量](https://leetcode.cn/problems/number-of-provinces/)代码题…...
思维导图+实现一个登录窗口界面
QQ2024122-205851 import sys from PyQt6.QtGui import QIcon, QPixmap, QMovie from PyQt6.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QLabel, QVBoxLayout# 封装我的窗口类 class LoginWidget(QWidget):# 构造函数def __init__(self):# 初始化父类su…...
多级缓存设计实践
缓存是什么? 缓存技术是一种用于加速数据访问的优化策略。它通过将频繁访问的数据存储在高速存储介质(如内存)中,减少对慢速存储设备(如硬盘或远程服务器)的访问次数,从而提升系统的响应速度和…...
ElasticSearch学习笔记一
目录 1.ElasticSearch-Head如何启动 2.ElasticSearch-Head创建索引 2.1创建索引时的“分片数”和“副本数”是什么意思? 类比 1:图书馆的书架与备份 类比 2:快递分拣中心与包裹副本 总结 编辑 2.2如何查看现有索引的分片数和备份数 …...
A058-基于Spring Boot的餐饮管理系统的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...
代码设计:设计模式:应对变化
文章目录 概述1.拆分代码2.解耦3.扩展总结概述 代码的设计模式主要为了应对变化 三种代码设计中应对变化的方式 1.拆分代码 2.解耦 3.扩展 1.拆分代码 减小变化对代码的影响 需要拆分代码的几种情况 1.类或方法的代码量巨大,导致代码可读性降低 2.存在复杂的代码,如…...
《Vue 路由导航:打造流畅的单页应用一》
一、Vue Router 简介 Vue Router 是什么? Vue Router 是 Vue.js 官方提供的路由管理器,用于构建单页面应用并实现页面间导航。它基于 Vue 的组件系统构建,通过配置路由将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面…...
【xshell连接失败】
xshell用pem连接失败 Warning: Identity file ”open_api_test.ssh not accessible: No such file or directory. Warning: Identity file open_api_test2.pem not accessible: No such file or directory. Permission denied (publickey,gssapi-keyex,gssapi-with-mic). 搞了半…...
说说Elasticsearch拼写纠错是如何实现的?
大家好,我是锋哥。今天分享关于【说说Elasticsearch拼写纠错是如何实现的?】面试题。希望对大家有帮助; 说说Elasticsearch拼写纠错是如何实现的? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Elasticsearch 中&…...
实例讲解MATLAB绘图坐标轴标签旋转
在进行绘图时需要在图片上添加上做标轴的标签,但是当数据量比较多时,例如一天24小时的数据,这时把每个小时显示在左边轴的标签上,文字内容放不下,因此需要将坐标轴标签旋转一定的角度,这样可以更好在图形上…...
Win10+Ubuntu20.04双系统重装Ubuntu22.04单系统
从去年 8 月美化 Ubuntu 系统后一直存在内核错误问题,但因为大部分功能还能正常使用,只是在 apt 时报错,所以一直逃避不想重装,直到最近 12 月新的开始,恰好设置的界面打不开得重装 gnome ,所以下定决心重装…...
eBPF:现代Linux的强大内核扩展技术
eBPF:现代Linux的强大内核扩展技术 eBPF(Extended Berkeley Packet Filter)是一项变革性的技术,它为Linux内核提供了一个灵活的、可编程的方式来处理数据包过滤、网络监控、安全性和系统性能分析等多种任务。 1. eBPF简介 1.1 …...
深度学习-52-AI应用实战之基于Yolo8的目标检测自动标注
文章目录 1 YOLOv81.1 YOLOV8的不同版本1.2 可检测类别1.3 数据说明1.4 网络结构1.5 算法核心步骤2 目标检测的基本原理2.1 安装yolov8(cpu版本)2.2 图片检测2.3 视频检测2.4 自动标注2.5 保存标注结果3 参考附录1 YOLOv8 YOLOv8是一种前沿的计算机视觉技术,它基于先前YOLO版…...
人工智能工作流程概述:从数据到智能决策
目录 一、引言 二、人工智能工作流程概述 (一)数据收集 (二)数据预处理 (三)模型选择与设计 (四)模型训练 (五)模型评估 (六)模…...
Jenkins升级到最新版本后无法启动
1. 场景还原 最近在web界面将jenkins升级到最新版本后,后台无法启动jenkins服务,服务状态如下: 运行jenkins命令提示invalid Java version jenkins --version jenkins: invalid Java version: java version "1.8.0_202" Java(TM)…...
.net core MVC入门(三)——Product页面添加
文章目录 项目地址一、Product数据库准备 项目地址 教程作者:教程地址: 代码仓库地址: 所用到的框架和插件: dbt airflow一、Product数据库准备 添加Product的EF上下文 public DbSet<Category> Categories { get; set; …...
【Java基础】笔记
List和ArrayList区别 public class Arrays_BugDemo {public static void main(String[] args){/** List 是一个固定大小的列表,虽然可以进行查询操作,但不支持添加、删除或修改元素。* 如果需要一个可以动态修改的列表,可以使用 ArrayList 进…...
Docker 进阶指南:常用命令、最佳实践与资源管理
Docker 进阶指南:常用命令、最佳实践与资源管理 Docker 作为一种轻量级的容器化技术,已经成为现代软件开发和部署不可或缺的工具。本文将为您深入介绍 Docker 的常用命令、最佳实践以及如何有效管理容器资源,帮助您更好地在 Ubuntu 22.04 或…...
C++编写静态库
1、新建项目创建静态库staticLib1. demoStaticLib.h #pragma once class ArrayTool { public:int Max(const int* lpHead, const int nLength);int Sum(const int* lpHead, const int nLength); }; demoStaticLib.cpp #include "pch.h" #include "demoStati…...
oracle数据库日常操作
1、执行SQL语句后不显示PL/SQL procedure successfully completed set feedback off; 2、显示实例名称 echo "set sqlprompt \"_user_connect_identifier> \"" >> $ORACLE_HOME/sqlplus/admin/glogin.sql 3、客户端尝试连接到服务器时发生超时 …...
行驶证 OCR 识别API接口的影响因素
一、影响因素 (一)证件质量与图像质量 行驶证的质量对OCR 识别 API 界面的效果有重要影响。清晰的文件可以使识别系统准确地捕获文本信息,而模糊的文本可能会导致识别错误。而且不同地区、不同年份的行驶证在字体、排版、格式等方面可能存在差异,甚至可…...
什么是TCP/IP和UDP
TCP/IP和UDP都是网络协议,用于不同计算机或设备之间的通信。它们的作用是定义数据如何在网络上传输,但它们在传输方式、可靠性和效率上有显著的区别。 1. TCP/IP TCP/IP(传输控制协议/互联网协议,Transmission Control Protocol…...
1074 Reversing Linked List (25)
Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K3, then you must output 3→2→1→6→5→4; if K4, you must output 4→3→2→1→5→6. Input Specif…...
layui table 纵向滚动条导致单元格表头表体错位问题
我用的时layui2.6.8版本 历史项目维护,bug给我让我做了,本来利用前端手段强解决,后来发现很多table 找了解决办法 打开layui-v2.6.8/lay/modules/table.js 如果打开后时压缩的代码 直接搜索 e.find(".layui-table-patch") …...