11 月 11 日 ROS 学习笔记——ROS 架构及概念

文章目录

  • 前言
  • 一、 ROS 文件系统级
      • 1). 工作空间 Ws
      • 2). 功能包
      • 3). 消息 msg
      • 4). 服务 srv
  • 二、计算图级
      • 1). 动态加载节点 nodelet
      • 2). 主题 topic
      • 3). 服务 srv
      • 4). 消息 msg
      • 5). 试用练习
      • 5). 创建工作空间
      • 6). 创建 ROS 功能包和元功能包
      • 7). 编译ROS功能包
      • 8). 使用 ROS 节点
      • 9). 使用主题与节点交互 rostopic
      • 10). 使用服务 rosservice
      • 11). 使用参数服务器 rosparam
      • 12). 创建节点
      • 13). 编译节点
      • 14). 创建 msg 和 srv 文件
      • 15). 使用 srv 和 msg 文件
      • 16). launch
      • 17). 动态参数


前言

本文为 11 月 11 日 ROS 学习笔记——ROS 架构及概念,分为 ROS 文件系统级和计算图级两节。


一、 ROS 文件系统级

  • 功能包 package:用于创建ROS程序的最小结构和最少内容,包含ROS运行进程(节点)、配置文件等,
  • 功能包清单 package.xml:提供关于功能包、 许可证、依赖关系、编译标志等的信息,
  • 元功能包 Metapackage: 几个具有某些功能的包组织在一起,
  • 元功能包清单:
  • 消息类型 msg type: 进程发送到其他进程的信息,
  • 服务类型 srv type: 为 ROS 中由每个进程提供的服务定义请求和响应数据结构。

ROS 文件级

1). 工作空间 Ws

工作空间就是一个文件夹,包含功能包,功能包又包含源文件和环境或工作空间,提供编译这些功能包的一种方式.

请添加图片描述

  • 源文件空间 src:放置功能包、项目、复制的包等。最重要的一个文件是 CMakeLists.txt,
  • 编译空间 build:为功能包和项目保存缓存信息、配置和其他中间文件。
  • 开发空间 devel:用来保存编译后的程序,无须安装就能用来测试的程序。

2). 功能包

功能包是一种特定结构的文件和文件夹组合,结构如下:

  • include/package_name/:包含需要的库的头文件,
  • msg/: 存放非标准信息,
  • scripts/: 存放Bash、Python或任何其他脚本语言的可执行脚本,
  • src/: 存储程序源文件,
  • srv/: 服务类型,
  • package.xml: 功能包清单文件。

package.xml 必须在每个功能包中,用来说明此包相关的各类信息,包括包的名称、依赖关系等信息。两个典型标记 <build_depend> 和 <run_depend>:

  • <build_depend>: 显示当前功能包安装之前必须先安装哪些功能包
  • <run_depend>: 显示运行功能包中代码所需要的包.

3). 消息 msg

消息类型必须具有:字段 field 和常量 constant,如:

  • int32 id
  • float32 vel
  • string name

4). 服务 srv

用以实现节点之间的请求/响应通信。


二、计算图级

ROS 创建一个连接到所有进程的网络。在系统中的任何节点都可以访问此网络,并通过该网络与其他节点交互,获取其他节点发送的信息,并将自身数据发布到网络上。

请添加图片描述

  • 节点 node:计算执行进程,与其他节点进行交互。最好让众多节点都具有单一的功能,而不是在系统中创建一个包罗万象的大节点。
  • 节点管理器 master:用于节点的名称注册和查找等,也设置节点间的通信。如果在整个ROS中没有节点管理器,就无法与节点、服务、消息等通信。
  • 参数服务器: parameter server: 通过参数,就能够在运行时配置节点或改变节点的工作任务。
  • 消息 message: 节点通过消息完成彼此的沟通。消息包含一个节点发送到其他节点的信息数据。
  • 主题 topic:每个消息都必须有一个名称以便被 ROS 网络分发。节点可以通过订阅某个主题,接收来自其他节点的消息。一个节点可以订阅一个主题,而不需要任何其他节点同时发布该主题。
  • 服务 service:服务能够允许我们直接与某个节点进行交互。
  • 消息记录包 bag:用于保存和回放ROS消息 数据的文件格式。

1). 动态加载节点 nodelet

内部可通信的多个节点,可以在单个进程中运行多个节点,每个nodelet为一个线程。可以在不使用 ROS 网络的情况下与其他节点通信,节点通信效率更高。 nodelet 对于摄像头和3D传感器这类数据传输量非常大的设备特别有用。

2). 主题 topic

节点间用来传输数据的总线。通过主题进行消息传输不需要节点之间直接连接,发布者和订阅者之间不需要知道彼此是否存在。一个主题可以有多个订阅者,也可以有多个发布者。每个主题都是强类型的,发布到主题上的消息必须与主题的 ROS 消息类型相匹配,并且节点只能接收类型匹配的消息:

  • TCP/IP:基于 TCP 传输称为 TCPROS,使用 TCP/IP 长连接,是ROS默认的传输方式;
  • UDP:UDPROS,是一种低延迟高效率的传输方式, 但可能产生数据丢失,最适合远程操控之类的任务。

3). 服务 srv

当需要直接与节点通信并以 RPC 方式获得应答时,将无法通过主题实现,而需要使用服务。服务需要由用户开发,节点并不提供标准服务。包含消息源代码的 文件存储在 srv 文件夹中。

服务类型是包名和 .srv 文件名的组合。例如 chapter2_tutorials/srv/chapter2_srv1.srv 文件的服务类型是 chapter2_tutorials/chapter2_srv1

4). 消息 msg

一个节点通过向特定主题发布消息,将信息发送到另一个节点。消息的类型在遵循以下标准命名方式:包名/文件名.msg, 例 如,std_msgs/msg/String.msg 的消息类型是 std_msgs/String

5). 试用练习

  • 查找 turtlesim 包的路径
rospack find turtlesim
>>> /opt/ros/noetic/share/turtlesim
  • 查找在系统中安装过的某个元功能包
rosstack find ros_comm
>>> /opt/ros/noetic/share/ros_comm
  • 获得功能包或功能包集下面的文件列表
rosls turtlesim/
>>> cmake  images  msg  package.xml  srv
  • 更改当前工作目录
roscd turtlesim/
pwd
>>> /opt/ros/noetic/share/turtlesim

5). 创建工作空间

  • 查看 ROS 正在使用的工作空间
echo $ROS_PACKAGE_PATH 
>>> /home/li/Documents/Demo01_Ws/src:/opt/ros/noetic/share
  • 新建此文件夹
mkdir -p ~/dev/catkin_ws/src
cd ~/dev/catkin_ws/src/
catkin_init_workspace
  • 编译工作空间
cd ~/dev/catkin_ws/
catkin_make
  • 完成配置
source devel/setup.bash

6). 创建 ROS 功能包和元功能包

cd ~/dev/catkin_ws/src/
catkin_create_pkg chapter2_tut std_msgs roscpp

7). 编译ROS功能包

cd ~/dev/catkin_ws/
catkin_make

8). 使用 ROS 节点

  • 启动一个新的节点
rosrun turtlesim turtlesim_node
  • 查看用于程序调试的信息
rosnode info /turtlesim

9). 使用主题与节点交互 rostopic

  • 使用箭头键移动海龟
rosrun turtlesim turtle_teleop_key
  • 使用以下命令行查看主题清单
rostopic list
>>> /rosout
	/rosout_agg
	/turtle1/cmd_vel
	/turtle1/color_sensor
	/turtle1/pose
  • 运行以下命令行并使用箭头键查看消息产生时发送了哪些数据
rostopic echo /turtle1/cmd_vel
>>> linear: 
	  x: 2.0
	  y: 0.0
	  z: 0.0
	angular: 
	  x: 0.0
	  y: 0.0
	  z: 0.0
---
  • 使用以下命令行查看由主题发送的消息类型
rostopic type /turtle1/cmd_vel
>>> geometry_msgs/Twist
  • 使用以下命令查看消息字段
rosmsg show geometry_msgs/Twist
>>> geometry_msgs/Vector3 linear
	  float64 x
	  float64 y
	  float64 z
	geometry_msgs/Vector3 angular
	  float64 x
	  float64 y
	  float64 z
  • 直接发布主题使海龟做圆周运动
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist "linear:
  x: 1.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 1.0" 

10). 使用服务 rosservice

服务是能够使节点之间相互通信的另一种方法。服务允许节点发送请求和接收响应

  • 列出活动服务
rosservice list
>>> /clear
	/kill
	/reset
	/rosout/get_loggers
	/rosout/set_logger_level
	/spawn
	/turtle1/set_pen
	/turtle1/teleport_absolute
	/turtle1/teleport_relative
	/turtlesim/get_loggers
	/turtlesim/set_logger_level
  • 查看某个服务的类型
rosservice type /clear
>>> std_srvs/Empty
  • 调用服务
rosservice call /clear 
  • 以不同的方向在另一个位置创建另一只海龟
rosservice type /spawn | rossrv show // 查看该服务的类型
>>> float32 x
	float32 y
	float32 theta
	string name
	---
	string name

rosservice call /spawn 3 3 0.2 "new_turtle" // 调用服务

11). 使用参数服务器 rosparam

参数服务器用于存储所有节点均可访问的数据。ROS中用来管理参数服务器的工具称为 rosparam.

  • 查看所有节点使用的服务器参数
rosparam list
>>> /rosdistro
	/roslaunch/uris/host_li_alienware__41685
	/rosversion
	/run_id
	/turtlesim/background_b
	/turtlesim/background_g
	/turtlesim/background_r
  • 读取参数的某个值
rosparam get /turtlesim/background_b
>>> 255
  • 设定一个新的值
rosparam set /turtlesim/background_b 100
  • 使用 dump 参数保存或加载参数服务器的内容
rosparam dump save.yaml
  • 使用 load 向参数服务器加载新的数据文件
rosparam load load.yaml namespace

12). 创建节点

创建两个节点:一个发布数据,另一个接收数据.

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>

/*
    发布方
*/

int main(int argc, char **argv)
{
    // 初始化节点
    ros::init(argc, argv, "example_a");
    // 进程的处理程序,它允许我们与环境交互
    ros::NodeHandle n;
    // 将节点实例化成发布者,将发布的主题和类型的名称告知节点管理器
    ros::Publisher chatter_pub = 
        n.advertise<std_msgs::String>("message", 1000);
    // 设置发送数据的频率
    ros::Rate loop_rate(10);

    while (ros::ok()) {
        // 创建消息变量
        std_msgs::String msg;
        std::stringstream ss;
        ss << "I am the example_a_node";
        msg.data = ss.str();

        // 继续发布消息
        chatter_pub.publish(msg);
        // spinOnce 在主循环中执行一次迭代允许用户执行操作
        ros::spinOnce();
        // 将程序挂起
        loop_rate.sleep();

    }
  
    return 0;
}
#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::init(argc, argv, "example_b");
    ros::NodeHandle n;

    // 创建一个订阅者,并从主题获取以message为名称的消息数据
    ros::Subscriber sub = n.subscribe("message", 1000,
                            chatterCallback);

    // 运行到这里时调用 chatterCallback 回调函数
    ros::spin();

    return 0;
}

13). 编译节点

  • 修改 CMakeLists.txt
add_executable(example1_a src/example1_a.cpp)
add_executable(example1_b src/example1_b.cpp)

add_dependencies(example1_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(example1_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(example1_a
  ${catkin_LIBRARIES}
)
target_link_libraries(example1_b
  ${catkin_LIBRARIES}
)
  • 启动 roscore,并在不同的命令行窗口下分别运行两个节点
roscore
rosrun demo01_pub_cli example1_a
rosrun demo01_pub_cli example1_b

>>> [ INFO] [1700051508.077304984]: I heard: [I am the example_a_node]
	[ INFO] [1700051508.177173843]: I heard: [I am the example_a_node]
	[ INFO] [1700051508.277239966]: I heard: [I am the example_a_node]
	...
  • 使用 rosnoderostopic 命令来调试和查看当前节点的运行状况
rosnode info /example_b
>> Node [/example_b]
	Publications: 
	 * /rosout [rosgraph_msgs/Log]
	
	Subscriptions: 
	 * /message [std_msgs/String]
	
	Services: 
	 * /example_b/get_loggers
	 * /example_b/set_logger_level
rostopic info /message 
>>> Type: std_msgs/String

	Publishers: 
	 * /example_a
	
	Subscribers: 
	 * /example_b
rostopic type /message
>>> std_msgs/String
rostopic bw /message
>>> subscribed to [/message]
	average: 296.19B/s
	        mean: 27.00B min: 27.00B max: 27.00B window: 10

14). 创建 msg 和 srv 文件

  • 首先创建一个新的 .msg 文件,添加:
int32 A
int32 B
int32 C
  • 编辑 package.xml ,取消注释:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
  • 编辑 CMakeList.txt,并编译
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

# Generate messages in the 'msg' folder
add_message_files(
  FILES
  chapter2_msg1.msg
)

# Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
)
  • 检查编译是否成功,如果看到与 .msg 文件中看到一样的内容,说明编译正确
rosmsg show demo01_pub_cli/chapter2_msg1
>>> int32 A
	int32 B
	int32 C
  • 创建一个新的 .srv 文件:
int32 A
int32 B
int32 C
---
int32 sum
  • 编辑 package.xml ,取消注释:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
  • 编辑 CMakeList.txt,并编译
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo01_pub_cli
 CATKIN_DEPENDS message_runtime
#  DEPENDS system_lib
)

# Generate messages in the 'msg' folder
add_message_files(
  FILES
  chapter2_msg1.msg
)

# Generate services in the 'srv' folder
add_service_files(
  FILES
  chapter2_srv1.srv
)

# Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
)
  • 检查编译是否成功,如果看到与 .msg 文件中看到一样的内容,说明编译正确
rossrv show demo01_pub_cli/chapter2_srv1
>>> int32 A
	int32 B
	int32 C
	---
	int32 sum

15). 使用 srv 和 msg 文件

该服务将对三个整数求和,需要两个节点:一个 服务器 和一个 客户端

#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_srv1.h"

/*
    服务端
*/

// 回调函数
bool add(demo01_pub_cli::chapter2_srv1::Request &req,
         demo01_pub_cli::chapter2_srv1::Response &res) {

    res.sum = req.A + req.B + req.C;
    ROS_INFO("request: A=%d, B=%d, C=%d", (int)req.A, (int)req.B,
                                          (int)req.C);
    ROS_INFO("sending back response: [%d]", (int)res.sum);

    return true;

}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "add_3_ints_server");
    ros::NodeHandle n;

    // 创建服务并在 ROS 中发布广播
    ros::ServiceServer service = n.advertiseService("add_3_ints", 
                                                    add);

    ROS_INFO("Ready to add 3 ints.");
    ros::spin();

    return 0;
}
#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_srv1.h"
#include <cstdlib>

/*
    客户端
*/


int main(int argc, char **argv)
{
    ros::init(argc, argv, "add_3_ints_client");
    if (argc != 4) {
        ROS_INFO("usage: add_3_ints_client A B C");
        return 1;
    }

    ros::NodeHandle n;

    // 创建客户端,名为 add_3_ints
    ros::ServiceClient client = 
        n.serviceClient<demo01_pub_cli::chapter2_srv1>("add_3_ints");
    
    // 创建 srv 请求类型的实例
    // 加入需要发送的数据值
    demo01_pub_cli::chapter2_srv1 srv;
    srv.request.A = atoll(argv[1]);
    srv.request.B = atoll(argv[2]);
    srv.request.C = atoll(argv[3]);
    
    // 调用服务并发送数据
    if (client.call(srv)) {
        ROS_INFO("Sum: %ld", (long int)srv.response.sum);
    } else {
        ROS_ERROR("Failed to call service add_3_ints");
        return 1;
    }

    return 0;
}
  • 编译 CMakeList.txt
add_executable(example2_a src/example2_a.cpp)
add_executable(example2_b src/example2_b.cpp)

add_dependencies(example2_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(example2_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(example2_a
  ${catkin_LIBRARIES}
)
target_link_libraries(example2_b
  ${catkin_LIBRARIES}
)
  • 启动节点
rosrun demo01_pub_cli example2_a
>>> [ INFO] [1700059229.228592862]: Ready to add 3 ints.
	[ INFO] [1700059253.580802115]: request: A=1, B=2, C=3
	[ INFO] [1700059253.580825139]: sending back response: [6]
rosrun demo01_pub_cli example2_b 1 2 3
>>> [ INFO] [1700059253.580914856]: Sum: 6
  • 创建发布和订阅节点
#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_msg1.h"
#include <sstream>

int main(int argc, char **argv)
{
    ros::init(argc, argv, "example3_a");
    ros::NodeHandle n;

    ros::Publisher pub = 
        n.advertise<demo01_pub_cli::chapter2_msg1>("message", 1000);
    ros::Rate loop_rate(10);

    while (ros::ok()) {
        demo01_pub_cli::chapter2_msg1 msg;

        msg.A = 1;
        msg.B = 2;
        msg.C = 3;
        pub.publish(msg);
        ros::spinOnce();

        loop_rate.sleep();

    }

    return 0;
}
#include "ros/ros.h"
#include "demo01_pub_cli/chapter2_msg1.h"


void messageCallback(const demo01_pub_cli::chapter2_msg1::ConstPtr& msg) {

    ROS_INFO("I heard: [%d] [%d] [%d]", msg->A, msg->B, msg->C);

}


int main(int argc, char **argv)
{
    ros::init(argc, argv, "example3_b");
    ros::NodeHandle n;

    ros::Subscriber sub = n.subscribe("message", 1000, 
                                    messageCallback);

    ros::spin();

    return 0;
}
  • 编译 CMakeList.txt
add_executable(example3_a src/example3_a.cpp)
add_executable(example3_b src/example3_b.cpp)

add_dependencies(example3_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(example3_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(example3_a
  ${catkin_LIBRARIES}
)
target_link_libraries(example3_b
  ${catkin_LIBRARIES}
)
  • 启动节点
rosrun demo01_pub_cli example3_a
rosrun demo01_pub_cli example3_b
>>> [ INFO] [1700061493.409080424]: I heard: [1] [2] [3]
	[ INFO] [1700061493.509095136]: I heard: [1] [2] [3]
	[ INFO] [1700061493.609056521]: I heard: [1] [2] [3]
	...

16). launch

用于启动多个节点。 当执行启动文件时,并不需要在 roscore 命令前启动,roslaunch 会启动它。当在 shell中只运行一个节点时,可以看到 ROS_INFO 输出的消息。但是当运行启动文件时,则看不到。

  • 创建 .launch 文件
<launch>
    <node name="example1_a" pkg="demo01_pub_cli" type="example1_a" />
    <node name="example1_b" pkg="demo01_pub_cli" type="example1_b" />
</launch>
  • 启动 .launch 文件
roslaunch demo01_pub_cli chapter2.launch
  • 查看运行的节点:
rosnode list
>>> /example1_a
	/example1_b
	/rosout
  • 运行 rqt_console 程序看到信息
rqt_console

rqt

17). 动态参数

配置一个包含动态重配置实用程序功能的基本节点。

  • 创建配置文件 .cfg
#! /usr/bin/env python
PACKAGE = "demo01_pub_cli"

# 初始化参数生成器
from dynamic_reconfigure.parameter_generator_catkin import *

gen = ParameterGenerator()

# 加入不同的参数类型并设置默认值、描述、范围等
"""
        gen.add()
                - name: 参数的名称
                - type: 参数值的类型
                - level: 一个传递给回调的位掩码
                - description: 描述
                - default: 节点启动时的默认值
                - min: 参数最小值
                - max: 参数最大值
"""
gen.add("double_param", double_t, 0, "A double parameter",
        .1, 0, 1)
gen.add("str_param", str_t, 0, "A string parameter", "Chapter2_dynamic_reconfigure")
gen.add("int_param", int_t, 0, "An Integer parameter", 1, 0, 100)
gen.add("bool_param", bool_t, 0, "A Boolean parameter", True)

size_enum = gen.enum([gen.const("Low", int_t, 0, "Low is 0"),
                      gen.const("Medium", int_t, 1, "Medium is 1"),
                      gen.const("High", int_t, 2, "High is 2")],
                      
                                "Select from the list")

gen.add("size", int_t, 0, "Select from the list", 1, 0, 3, edit_method=size_enum)

# 生成必要的文件并退出程序
exit(gen.generate(PACKAGE, "demo01_pub_cli", "chapter2_"))
  • 修改执行文件的权限
chmod +x *.cfg
  • 修改 CMakeList.txt 并编译
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
  dynamic_reconfigure
)

generate_dynamic_reconfigure_options(
  cfg/chapter2.cfg
)

add_dependencies(example4 demo01_pub_cli_gencfg)
  • 写具有动态重配置支持的新节点
#include <ros/ros.h>
#include <dynamic_reconfigure/server.h>
#include <demo01_pub_cli/chapter2_Config.h>

// 参数访问的方式, 将输出参数的新值
void callback(demo01_pub_cli::chapter2_Config &config, uint32_t level) {
    ROS_INFO("Reconfigure Request: %d, %f, %s %s %d",
            config.int_param,
            config.double_param,
            config.str_param.c_str(),
            config.bool_param?"True":"False",
            config.size);

}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "example4_dynamic_reconfigure");

    // 初始化服务器
    dynamic_reconfigure::Server<demo01_pub_cli::chapter2_Config> server;
    dynamic_reconfigure::Server<demo01_pub_cli::chapter2_Config>::CallbackType f;

    f = boost::bind(&callback, _1, _2);

    // 向服务器发送callback函数。当服务器得到重新配置请求时调用 callback 函数
    server.setCallback(f);

    ros::spin();
    return 0;
}
  • 修改 CMakeLists.txt , 编译并运行节点和动态重配置 GUI
add_executable(example4 src/example4.cpp)
add_dependencies(example4 demo01_pub_cli_gencfg)
rosrun demo01_pub_cli example4
>>> [ INFO] [1700312052.497444818]: Reconfigure Request: 1, 0.100000, Chapter2_dynamic_reconfigure True 1
rosrun rqt_reconfigure rqt_reconfigure
>>> [ INFO] [1700312097.307494990]: Reconfigure Request: 42, 0.100000, Chapter2_dynamic_reconfigure True 1
	[ INFO] [1700312099.355379827]: Reconfigure Request: 42, 0.470000, Chapter2_dynamic_reconfigure True 1
	[ INFO] [1700312101.253127436]: Reconfigure Request: 73, 0.470000, Chapter2_dynamic_reconfigure True 1
	[ INFO] [1700312103.210104907]: Reconfigure Request: 73, 0.710000, Chapter2_dynamic_reconfigure True 1

rqt_reconfigure


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/162095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

kubernetes|云原生| 如何优雅的重启和更新pod---pod生命周期管理实务

前言&#xff1a; kubernetes的管理维护的复杂性体现在了方方面面&#xff0c;例如&#xff0c;&#xff50;&#xff4f;&#xff44;的管理&#xff0c;服务的管理&#xff0c;用户的管理&#xff08;&#xff32;&#xff22;&#xff21;&#xff23;&#xff09;&#xf…

linux进程间通信之信号

摘要 本文旨在研究Linux进程间通信的机制之一&#xff1a;信号。信号是由操作系统来处理的&#xff0c;说明信号的处理在内核态。信号不一定会立即被处理&#xff0c;此时会储存在信 号的信号表中。最后&#xff0c;我们会对这种通信方式的优缺点进行全面的分析&#xff0c;并给…

手搓哈希表、列表、队列,只为了用C语言快速求解华容道游戏,我不是大佬,只是一个游戏算法爱好者

背景 多年前曾经写过C语言求解华容道&#xff0c;当时没有用到哈希表&#xff0c;导致整个查重搜索数组过大&#xff0c;每次求解都得花上数分钟的时间&#xff0c;如今时过境迁&#xff0c;对数据结构和算法有了更深的理解&#xff0c;所以得把这一块补上了。(其实就是最近想…

POSIX信号量

POSIX信号量 POSIX信号量用于同步操作&#xff0c;达到无冲突访问临界资源的目的&#xff0c;可以用于线程之间的通信。而信号量的本质其实就是一把计数器&#xff01;&#xff01;而我们对计数器有2个操作&#xff0c;一个是增加计数器的值&#xff0c;一个是减少计数器的值。…

MySQL/Oracle用逗号分割的id怎么实现in (逗号分割的id字符串)。find_in_set(`id`, ‘1,2,3‘) 函数,

1.MySQL 1.1.正确写法 select * from student where find_in_set(s_id, 1,2,3); 1.2.错误示范 select * from student where find_in_set(s_id, 1,2 ,3); -- 注意&#xff0c;中间不能有空格。1、3 select * from student where find_in_set(s_id, 1,2, 3); -- 注意…

Nginx安装配置与SSL证书安装部署

一、Nginx Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;被广泛用于构建现代化的Web应用和提供静态内容。 nginx官网 这里下载nginx-1.24.0-zip Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;被广泛用于构建现代化的Web应用和提供静态内…

腾讯云新用户专享买什么服务器划算?腾讯云新用户服务器购买建议

腾讯云近期推出了一系列的轻量应用服务器活动&#xff0c;其中包括了两款三年时长的优惠配置。这两款配置分别是2核2G4M和2核4G5M&#xff0c;价格分别为540元和756元。相较于按月购买的费用&#xff0c;三年时长的套餐更加划算&#xff0c;并且能够有效省下续费费用。所以&…

【LeetCode刷题-滑动窗口】-- 239.滑动窗口最大值

239.滑动窗口最大值 分析&#xff1a; 方法&#xff1a;优先队列 对于最大值&#xff0c;可以使用优先队列(堆)&#xff0c;其中的大根堆可以帮助实时维护一系列元素中的最大值 在本题中&#xff0c;初始时&#xff0c;将数组nums的前k个元素放入优先队列中&#xff0c;每当我…

【JavaEE初阶】 CSS的引入方式和选择器

文章目录 &#x1f332;CSS是什么&#xff1f;&#x1f340;CSS基础语法规范&#x1f384;引入方式&#x1f6a9;内部样式表&#x1f6a9;行内样式表&#x1f6a9;外部样式 &#x1f334;代码风格&#x1f333;选择器&#x1f6a9;选择器的种类&#x1f6eb;基础选择器&#x1…

【代码随想录】算法训练计划23

1、669. 修剪二叉搜索树 题目&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&…

Windows安装Java环境(OracleJDK)

在下载之前&#xff0c;我们先了解一下java的前世今生 1991年&#xff1a;Java 的前身 Oak 由 James Gosling 和他的团队在 Sun Microsystems 公司开发。1995年&#xff1a;Oak 更名为 Java&#xff0c;并在同年发布。Java 1.0 版本正式推出。1996年&#xff1a;Sun Microsyst…

springboot项目中没有识别到yml文件解决办法

springboot项目中没有识别到yml文件解决办法 ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传] 1、这个意思就是没有配置数据库的数据源路径。所以需要配置数据源&#xff0c;比如mysql的驱动和路径。检查是否在properties或者yml文件中是否已经配置好。…

车载开发岗位如何?Android程序员是否转行

这几年的大环境让大家都过的不安逸&#xff1b;社会动荡与就业问题一直困扰了不少人。在大家都认为的高薪工作程序员行业中&#xff0c;现在也是费力不讨好裁员风潮大部分指向互联网。 我们Android开发基本上已经感受很久了&#xff0c;就这就业问题很难存活。对此我们的目光都…

CI/CD - jenkins

目录 一、部署 1、简介 2、部署 二、配置 三、实时触发 四、自动化构建docker镜像 五、通过ssh插件交付任务 六、添加jenkins节点 七、RBAC 八、pipeline 九、jenkins结合ansible参数化构建 1、安装ansible 2、新建gitlab项目 3、jenkins新建项目playbook 一、部…

使用Lychee搭建个人图片存储系统并进行远程访问设置实现公网访问本地私人图床

文章目录 1.前言2. Lychee网站搭建2.1. Lychee下载和安装2.2 Lychee网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 图床作为图片集中存放的服务网站&#xff0c;可以看做是云存储的一部分&#xff0c;既可…

LeetCode Hot100之十:239.滑动窗口最大值

题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 提示&#xff1a; 1 < nums.length < 10^5 -10^4 < nums[i…

管理类联考——逻辑——知识+记忆篇——综合推理——考点+记忆

文章目录 整体目录大纲法汇总分类法记忆宫殿法绘图记忆法 考点记忆/考点汇总——按大纲 局部数字编码法归类记忆法重点记忆法歌决记忆法谐音记忆法理解记忆法比较记忆法 本篇思路&#xff1a;根据各方的资料&#xff0c;比如名师的资料&#xff0c;按大纲或者其他方式&#xff…

【新闻稿】Solv 与 zCloak 联合开发跨境贸易场景下可编程数字凭证项目,获得新加坡、加纳两国央行支持...

关于昨天 Solv 携手 zCloak 与新加坡和加纳两个央行合作的 Project DESFT&#xff0c;很多朋友都发来恭喜和祝福&#xff0c;并希望了解详情。这个事我们秘密努力了半年多&#xff0c;终于有一个阶段性的成果。这里我转载中文版官宣新闻稿&#xff0c;欢迎大家关注。等我忙过这…

无需公网IP、简单3步,直连远程NAS实现高速访问

面对NAS远程访问难题 蒲公英旁路组网盒子X1 一招搞定&#xff01; 无需公网IP、无需设置原有路由 简单3步&#xff0c;即可实现异地组网 更有点对点直连&#xff08;P2P&#xff09;模式 不限流量、不限速 传输速率取决于实际网络带宽 贝锐蒲公英X1&#xff0c;无需改变原…

mac系统安装docker desktop

Docker的基本概念 Docker 包括三个基本概念: 镜像&#xff08;Image&#xff09;&#xff1a;相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。比如说nginx,mysql,redis等软件可以做成一个镜像。容器&#…