机器人操作系统ROS(15):nodelet

ROS的数据通信是以XML-RPC的方式,在graph结构中以topic,service和param的方式传输数据,天生的数据交互存在一定的延时和阻塞。

Nodelet 包就是改善这一状况设计的, 使得多个算法运行在同一个过程中,并且算法间数据传输无需拷贝就可实现。 简单的讲就是可以将以前启动的多个node捆绑在一起manager,使得同一个manager里面的topic的数据传输更快,数据通讯中roscpp采用boost shared pointer方式进行publish调用,实现zero copy。  The nodelet package is designed to provide a way to run multiple algorithms in the same process with zero copy transport between algorithms. This package provides both the nodelet base class needed for implementing a nodelet, as well as the NodeletLoader class used for instantiating nodelets.

Regular nodes use TCP and this works fine for a lot of things. But if you have multiple processes that need to use messages that contain large amounts of data (like images or point clouds) packaging the message, sending it, then unpacking it can take a bit of time.
So if the two processes are on the same computer, it is quicker to just send a pointer to that data rather than sending the data itself over TCP. This only works for processes (aka nodelets) on the same computer though since a pointer for one computer doesn’t make sense for another computer.
So nodelets don’t make the processes quicker, but it is a quicker way to get information from one process to another.

TCPROS style

BUT, I recommend you don’t use nodelet. If you’re thinking of using it, you might want to first consider just launching two threads in a single node, instead of two nodes. If you create two threads in a single node, it will be more memory efficient than launching two “nodelets” in ROS.
e.g. REF: kobuki Tutorials Write your own controller for Kobuki

特点
1 nodelets间数据传输zero copy,有效避免数据copy和网络传输代价
2 支持pulgin的方式动态加载
3  使用C++ ROS的接口方式

nodelet 基类
1 定义基类nodelet::Nodelet, 任何nodelet继承自它可以使用plugin的方式动态加载。
2 提供命名空间,参数可remap
3  nodelet的manager的过程可以加载更多的nodelets. 同一个manager过程的nodelet数据传输zero copy .

Basic Usage
nodelet usage:
nodelet load pkg/Type manager – Launch a nodelet of type pkg/Type on manager manager
nodelet standalone pkg/Type   – Launch a nodelet of type pkg/Type in a standalone nod
nodelet unload name manager   – Unload a nodelet a nodelet by name from manager
nodelet manager               – Launch a nodelet manager node

Bring up a manager
A nodelet will be run inside a NodeletManager. A nodelet manager is a c++ program which is setup to listen to ROS services and will be the executable in which the nodelet is dynamically loaded. In this case we will run a standalone manager, but in many cases these managers will be embedded within running nodes.
$ rosrun nodelet nodelet manager __name:=nodelet_manager

Launching the nodelet
The nodelet executable, called here, will contact the nodelet_manager and ask it to instantiate an instance of the nodelet_tutorial_math/Plus nodelet. It will pass through the name, nodelet1, and also any remappings if applied to the code inside the nodelet. And parameters appear in the right namespace too.
$ rosrun nodelet nodelet load nodelet_tutorial_math/Plus nodelet_manager __name:=nodelet1 nodelet1/in:=foo _value:=1.1

机器人操作系统ROS(13):service of service和client

service和client的C++和Python实现方式。

  • 10、先建立消息msg和服务srv基础

rosmsg/rossrv/roscp可用于生成msg和srv文件,msg文件描述message,srv文件描述request/response服务。
msg文件有可能包括header,header由时间戳和坐标组成,例如:
Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist
srv文件也类似于msg文件,包括由’—‘分开的request和response两部分,例如:
int64 A
int64 B

int64 Sum
10.1、建立一个简单的msg文件:
$ cd ~/catkin_ws/src/beginner_tutorials
$ mkdir msg
$ echo “int64 num” > msg/Num.msg
1.2、修改package.xml文件,由于build时候需要”message_generation”,运行时候需要 “message_runtime”,所以要在package.xml文件中添加这两者:
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
1.3、修改CMakeLists.txt,在find_package call中添加message_generation的依赖:
catkin_package(
roscpp
rospy
std_msgs
message_generation
…)
1.4、修改CMakeLists.txt,添加运行时的依赖:
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES beginner_tutorials
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
1.5、修改CMakeLists.txt,添加一下代码块:
add_message_files(
FILES
Num.msg
)
1.6、编辑generate_
generate_messages(
DEPENDENCIES
std_msgs
)
1.7、验证:
$ rosmsg show beginner_tutorials/Num
/int64 num

2、建立一个简单的srv文件,功能是返回两个数的和:
$ roscd beginner_tutorials
$ mkdir srv
$ vi AddTwoInts.srv
int64 a
int64 b

int64 sum
2.2、如果有必要,修改package.xml文件,添加这build和runtime时候的消息:
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
2.3、如果必要,修改CMakeLists.txt,在find_package call中添加message_generation的依赖:
catkin_package(
roscpp
rospy
std_msgs
message_generation
…)
2.4、修改CMakeLists.txt,添加一下代码块:
add_service_files(
FILES
AddTwoInts.srv
)
2.5、可以检查:
$ rossrv show AddTwoInts
int64 a
int64 b

int64 sum

  • 14、service 和client(C++)

1、建立service的node:
$ cd ~/catkin_ws/src/beginner_tutorials/src
$ vi add_two_ints_server.cpp
#include “ros/ros.h”
#include “beginner_tutorials/AddTwoInts.h”//由以前AddTwoInts.srv生成的头文件
bool add(beginner_tutorials::AddTwoInts::Request  &req,beginner_tutorials::AddTwoInts::Response &res) { //提供的服务,request和response的类型由AddTwoInts.srv文件定义,返回类型是boolean。
res.sum = req.a + req.b;
ROS_INFO(“request: x=%ld, y=%ld”, (long int)req.a, (long int)req.b);
ROS_INFO(“sending back response: [%ld]”, (long int)res.sum);
return true;
}
int main(int argc, char **argv) {
ros::init(argc, argv, “add_two_ints_server”);
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService(“add_two_ints”, add);//服务建立,并广播
ROS_INFO(“Ready to add two ints.”);
ros::spin();
return 0;
}
2、vi add_two_ints_client.cpp,建立client的node:
#include “ros/ros.h”
#include “beginner_tutorials/AddTwoInts.h”
#include <cstdlib>
int main(int argc, char **argv) {
ros::init(argc, argv, “add_two_ints_client”);
if (argc != 3) {
ROS_INFO(“usage: add_two_ints_client X Y”);
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>(“add_two_ints”); //建立ros::ServiceClient对象,用于后面调用服务
beginner_tutorials::AddTwoInts srv; //实例化这个类
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
if (client.call(srv)) //调用服务
ROS_INFO(“Sum: %ld”, (long int)srv.response.sum);
else {
ROS_ERROR(“Failed to call service add_two_ints”);
return 1;
}
return 0;
}
3、如果必要,修改CMakeLists.txt:
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
4、build:
$ cd ~/catkin_ws
$ catkin_make
4、测试
运行service
$ rosrun beginner_tutorials add_two_ints_server
[ INFO] [1465192131.709699255]: Ready to add two ints.

运行client
$ rosrun beginner_tutorials add_two_ints_client 1 3
[ INFO] [1465192219.751503941]: request: x=1, y=3
[ INFO] [1465192219.751688678]: sending back response: [4]

  • 15、service 和client(Python)

1、建立service的node:
$ cd ~/catkin_ws/src/beginner_tutorials/scripts
$ nano add_two_ints_server.py
#!/usr/bin/env python
from beginner_tutorials.srv import *
import rospy

def handle_add_two_ints(req):
print “Returning [%s + %s = %s]”%(req.a, req.b, (req.a + req.b))
return AddTwoIntsResponse(req.a + req.b)

def add_two_ints_server():
rospy.init_node(‘add_two_ints_server’)
s = rospy.Service(‘add_two_ints’, AddTwoInts, handle_add_two_ints) #name,type,func_handler with AddTwoIntsRequest
print “Ready to add two ints.” #keeps your code from exiting until the service is shutdown.
rospy.spin()

if __name__ == “__main__”:
add_two_ints_server()

2、建立cilnet的node:
$ nano add_two_ints_client.py
#!/usr/bin/env python
import sys
import rospy
from beginner_tutorials.srv import *

def add_two_ints_client(x, y):
rospy.wait_for_service(‘add_two_ints’) # one way of calling services, blocks until the service named add_two_ints is available
try:
add_two_ints = rospy.ServiceProxy(‘add_two_ints’, AddTwoInts)
resp1 = add_two_ints(x, y)
return resp1.sum
except rospy.ServiceException, e:
print “Service call failed: %s”%e

def usage():
return “%s [x y]”%sys.argv[0]

if __name__ == “__main__”:
if len(sys.argv) == 3:
x = int(sys.argv[1])
y = int(sys.argv[2])
else:
print usage()
sys.exit(1)
print “Requesting %s+%s”%(x, y)
print “%s + %s = %s”%(x, y, add_two_ints_client(x, y))

3、测试
加权限:
$ chmod +x add_two_ints_server.py
$ chmod +x add_two_ints_client.py
build:
$ cd ~/catkin_ws
$ catkin_make
刷新:
$ source ~/.bashrc
运行:
$ rosrun beginner_tutorials add_two_ints_server.py

$ rosrun beginner_tutorials add_two_ints_client.py 4 5

机器人操作系统ROS(08):msg of publisher和subscriber

publisher和subscriber的C++和Python实现。

  • 11、Publisher和Subscriber(C++语言)

节点node通常就是指连接到ROS网络的一些可执行节点,常见的例如发布者publisher和订阅者subscruber。
1、建一个不停广播消息的发布者(讲者)node。在src下编辑talker.cpp:
$ cd ~/catkin_ws/src/beginner_tutorials
$ mkdir -p ~/catkin_ws/src/beginner_tutorials/src
$ vi talker.cpp
#include “ros/ros.h”
#include “std_msgs/String.h”
#include
int main(int argc, char **argv) {
ros::init(argc, argv, “talker”);//第三个参数是node名称
ros::NodeHandle n;//这是与ROS系统通讯的句柄,注意构造和析构
ros::Publisher chatter_pub = n.advertise<std_msgs::String>(“chatter”, 1000);//消息类型是std_msgs/String ,第二个参数是最大缓存消息数目,该函数生成一个publisher对象,该对象有一个比较重要的方法是publish()
ros::Rate loop_rate(10);//循环周期是10Hz
int count = 0;
while (ros::ok()) {
std_msgs::String msg;//消息对象
std::stringstream ss;
ss << “hello world ” << count;
msg.data = ss.str();
ROS_INFO(“%s”, msg.data.c_str());
chatter_pub.publish(msg);//消息类型要符合advertise<>()
ros::spinOnce();//这个主要用于回调
loop_rate.sleep();//休眠,直到满足10Hz
++count;
}
return 0;
}
2、建一个订阅者(讲者)node。在src下编辑listener.cpp:
#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, “listener”);
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe(“chatter”, 1000, chatterCallback);//返回一个ros::Subscriber对象,第二个参数是缓存消息最大数目
ros::spin();//进入循环
return 0;
}
3、编辑CMakeLists.txt,添加:
include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
这样就建立了两个可执行的talker和listener,然后可以make:
$ catkin_make
4、可以测试:
$ cd ~/catkin_ws
$ source ./devel/setup.bash
$ roscore
运行publisher:
$ rosrun beginner_tutorials talker
[INFO] [WallTime: 1314931831.774057] hello world 1314931831.77
[INFO] [WallTime: 1314931832.775497] hello world 1314931832.77

运行subscriber:
$ rosrun beginner_tutorials listener
[INFO] [WallTime: 1314931969.258941] /listener_17657_1314931968795I heard hello world 1314931969.26
[INFO] [WallTime: 1314931970.262246] /listener_17657_1314931968795I heard hello world 1314931970.26

13、Publisher和Subscriber(Python语言)
1、如果需要,建立工作区:
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
cd ..
catkin_make
2、如果需要,建立类似hello_word这样一个package:
cd ~/catkin_ws/src
catkin_create_pkg hello_world std_msgs rospy
cd ..
catkin_make
source ~/catkin_ws/devel/setup.bash 或者
echo “source ~/catkin_ws/devel/setup.bash” >> ~/.bashrc
3、建立保存脚本的文件夹:
mkdir scripts
4、发布节点hello_world_publisher.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher(‘hello_pub’, String, queue_size = 10) #give topic name and define msg type
rospy.init_node(‘hello_world_publisher’, anonymous = True) #give node name
r = rospy.Rate(10) #10hz, create a Rate object rate.
while not rospy.is_shutdown():
str = “hello world %s” % rospy.get_time()
rospy.loginfo(str)
pub.publish(str)
r.sleep()
if __name__ == ‘__main__’:
try:
talker()
except rospy.ROSInterruptException:
pass
Python脚本对缩进敏感,如图:

def定义了一个函数。if __name__==’__main__’说明仅在直接在CML下面才运行,try-except-pass掉来自ros的exception。
5、订阅者节点hello_world_subscriber.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def callback(data):
rospy.loginfo(rospy.get_caller_id() + “I heard %s”, data.data)
def listener():
rospy.init_node(‘hello_world_subscriber’, anonymous=True)
rospy.Subscriber(“hello_pub”, String, callback)
rospy.spin()
if __name__ == ‘__main__’:
listener()
6、可以测试
运行发布者node:
rosrun hello_world hello_world_publisher.py

运行订阅者node:
rosrun hello_world hello_world_subscriber.py