本文为 11 月 11 日 ROS 学习笔记——ROS 架构及概念,分为 ROS 文件系统级和计算图级两节。
工作空间就是一个文件夹,包含功能包,功能包又包含源文件和环境或工作空间,提供编译这些功能包的一种方式.
功能包是一种特定结构的文件和文件夹组合,结构如下:
package.xml 必须在每个功能包中,用来说明此包相关的各类信息,包括包的名称、依赖关系等信息。两个典型标记
消息类型必须具有:字段 field 和常量 constant,如:
用以实现节点之间的请求/响应通信。
ROS 创建一个连接到所有进程的网络。在系统中的任何节点都可以访问此网络,并通过该网络与其他节点交互,获取其他节点发送的信息,并将自身数据发布到网络上。
内部可通信的多个节点,可以在单个进程中运行多个节点,每个nodelet为一个线程。可以在不使用 ROS 网络的情况下与其他节点通信,节点通信效率更高。 nodelet 对于摄像头和3D传感器这类数据传输量非常大的设备特别有用。
节点间用来传输数据的总线。通过主题进行消息传输不需要节点之间直接连接,发布者和订阅者之间不需要知道彼此是否存在。一个主题可以有多个订阅者,也可以有多个发布者。每个主题都是强类型的,发布到主题上的消息必须与主题的 ROS 消息类型相匹配,并且节点只能接收类型匹配的消息:
当需要直接与节点通信并以 RPC 方式获得应答时,将无法通过主题实现,而需要使用服务。服务需要由用户开发,节点并不提供标准服务。包含消息源代码的 文件存储在 srv 文件夹中。
服务类型是包名和 .srv 文件名的组合。例如 chapter2_tutorials/srv/chapter2_srv1.srv
文件的服务类型是 chapter2_tutorials/chapter2_srv1
一个节点通过向特定主题发布消息,将信息发送到另一个节点。消息的类型在遵循以下标准命名方式:包名/文件名.msg
, 例 如,std_msgs/msg/String.msg
的消息类型是 std_msgs/String
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
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
cd ~/dev/catkin_ws/src/
catkin_create_pkg chapter2_tut std_msgs roscpp
cd ~/dev/catkin_ws/
catkin_make
rosrun turtlesim turtlesim_node
rosnode info /turtlesim
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"
服务是能够使节点之间相互通信的另一种方法。服务允许节点发送请求和接收响应
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" // 调用服务
参数服务器用于存储所有节点均可访问的数据。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
创建两个节点:一个发布数据,另一个接收数据.
#include "ros/ros.h"
#include "std_msgs/String.h"
#include
/*
发布方
*/
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;
}
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
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]
...
rosnode
和 rostopic
命令来调试和查看当前节点的运行状况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
int32 A
int32 B
int32 C
<build_depend>message_generationbuild_depend>
<exec_depend>message_runtimeexec_depend>
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
)
rosmsg show demo01_pub_cli/chapter2_msg1
>>> int32 A
int32 B
int32 C
int32 A
int32 B
int32 C
---
int32 sum
<build_depend>message_generationbuild_depend>
<exec_depend>message_runtimeexec_depend>
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
)
rossrv show demo01_pub_cli/chapter2_srv1
>>> int32 A
int32 B
int32 C
---
int32 sum
该服务将对三个整数求和,需要两个节点:一个 服务器 和一个 客户端 。
#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
/*
客户端
*/
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;
}
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
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;
}
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]
...
用于启动多个节点。 当执行启动文件时,并不需要在 roscore
命令前启动,roslaunch
会启动它。当在 shell中只运行一个节点时,可以看到 ROS_INFO
输出的消息。但是当运行启动文件时,则看不到。
<launch>
<node name="example1_a" pkg="demo01_pub_cli" type="example1_a" />
<node name="example1_b" pkg="demo01_pub_cli" type="example1_b" />
launch>
roslaunch demo01_pub_cli chapter2.launch
rosnode list
>>> /example1_a
/example1_b
/rosout
rqt_console
程序看到信息rqt_console
配置一个包含动态重配置实用程序功能的基本节点。
#! /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
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
#include
#include
// 参数访问的方式, 将输出参数的新值
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;
}
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