友情提示:380元/半年,儿童学编程,就上码丁实验室。
目 录
ROS 通信类型有Topic、Service和Action:
1.3.1 ROS话题(Topics)
话题(topics)实现了发布/订阅 (publish/subscribe)的通信机制,这是分布式系统数据交互的最常用方式之一。节点(node)通过话题发布数据之前,需要声明话题的名称和消息类型,节点通过订阅话题能够接收该话题的数据。例如,在一个系统中,微型飞行器通过板载计算机的节点读取摄像机的图像并发布到某个话题, 地面站的计算机通过订阅该话题可获取图像,用于位姿估计、3维重构、场景识别等任务。
在ROS中,某个话题的消息必须具有相同的数据类型,即一个话题名称对应预设好的消息类型。话题名称一般描述了发布的消息,例如,PR2机器人的话题“/wide_stereo/right/image_color”用于传递广角的立体摄像机的右侧的彩色图。
话题发布器(Topic Publisher)用于管理话题的消息发布,通过调用” NodeHandle::advertise()”创建,将该话题注册到控制节点,例如:
ros::Publisher chatter_pub = node.advertise<std_msgs::String>("chatter", 1000);
其中,第1个参数”chatter”为话题名称,第2个参数1000为队列长度。一旦超出给定话题的所有发布器的定义范围,将无法发布该话题消息。调用发布器的publish()方法可以将消息发布到话题,例如:
std_msgs::String msg; chatter_pub.publish(msg);
其中,消息类型应与创建发布器时的模板参数指定的消息类型一致,此处为“std_msgs::String”。
1.3.2 发布器和订阅器
接下来,创建一个包(package)实现两个节点,一是”talker”节点发布消息到话题”chatter”,二是“listener”节点读取“chatter”的消息并输出到终端。
首先,创建包”chat_pkg”:
cd ~/catkin_ws/src/ros_tutorial/
mkdir lecture1-3
cd lecture1-3
catkin_create_pkg chat_pkg std_msgs rospy roscpp
然后创建”talker”节点,在“~/catkin_ws/src/ros_tutorial/lecture1-3/src”路径下,创建C++源文件“talker.cpp”,输入如下内容:
#include "ros/ros.h"
#include “std_msgs/String.h”
#include <sstream>
int main(intargc, char **argv)
{
ros::init(argc, argv, “talker”); //Initiate new ROS node named “talker”
ros::NodeHandlenode;
ros::Publisherchatter_pub = node.advertise<std_msgs::String>(“chatter”,1000);
ros::Rateloop_rate(10);
intcount = 0;
while (ros::ok())//Keep spinning loop until user presses Ctrl+C
{
std_msgs::Stringmsg;
std::stringstreamss;
ss << “helloworld “<< count;
msg.data =ss.str();
ROS_INFO(“%s”, msg.data.c_str());
chatter_pub.publish(msg);
ros::spinOnce(); //Need to call this function often to allow ROS to process incoming messages
loop_rate.sleep(); //Sleep for the rest of the cycle, to enforce the loop rate
count++;
}
return 0;
}
调用节点句柄的subscribe()方法,可以创建订阅器(Subcriber)用于订阅某个话题的消息。例如:
ros::Subscriber sub = node.subscribe("chatter", 1000, messageCallback);
第1个参数“chatter”为话题名称;
第2个参数“1000”为队列长度;
第3个参数”messageCallback”为处理消息的函数。
下面创建“listener ”节点,在“~/catkin_ws/src/ros_tutorial/lecture1-3/src”路径下,创建C++源文件“listener.cpp”,输入如下内容:
#include “ros/ros.h”
#include “std_msgs/String.h”
// Topic messages callback
voidchatterCallback(conststd_msgs::String::ConstPtr&msg)
{
ROS_INFO(“Iheard: [%s]“,msg->data.c_str());
}
int main(intargc, char **argv)
{
//Initiate a new ROS node named “listener”
ros::init(argc, argv, “listener”);
ros::NodeHandlenode;
//Subscribe to a given topic
ros::Subscribersub = node.subscribe(“chatter”,1000, chatterCallback);
//Enter a loop, pumping callbacks
ros::spin();
return 0;
}
ros::spin() vs ros::spinOnce()
ros::spin()和ros::spinOnce()函数的区别,总的来说ros::spin()调用后不返回,只要有回调函数就执行,而ros::spinOnce()只执行一次,即只执行回调队列中的第一个函数,下一次调用ros::spinOnce()时再执行第2个回调函数。所以,ros::spin()后的程序,只有 ros::shutdown() 或 Ctrl+C才会执行;ros::spinOnce()则一般要放到循环中(例如:lecture1-2中的hello.c)。其它参考链接:
https://blog.csdn.net/liweibin1994/article/details/53084306
https://www.cnblogs.com/liu-fa/p/5925381.html
将如下内容添加到“chat_pkg”包下的CMakeLists.txt文件。
cmake_minimum_required(VERSION2.8.3)
project(chat_pkg)
…
## Declare a cppexecutable
add_executable(talkersrc/Talker.cpp)
add_executable(listenersrc/Listener.cpp)
## Specify libraries to link a library orexecutable target against
target_link_libraries(talker${catkin_LIBRARIES})
target_link_libraries(listener${catkin_LIBRARIES})
使用“catkin_make”工具构建包并编译所有的节点:
cd ~/catkin_ws/
catkin_make
至此,创建2个可执行文件(节点):talker和listener,在“catkin_ws/src/lib/chat_pkg”路径里。
注意,以上程序可以在github上获取,若是之前的教程“git clone”过程序,那么只需:
cd ~/catkin_ws/src/ros_tutorial
git pull
cd ~/catkin_ws
catkin_make
若首次”git clone”,则:
cd ~/catkin_ws/src/
git clone https://github.com/LinHuican/ros_tutorial
cd ~/catkin_ws
catkin_make
在终端运行节点,分别在三个终端运行如下指令:
roscore
rosrun chat_pkg talker
rosrun chat_pkg listener
不出意外的话,将输出类似如下的结果:
此时,可以使用“rosnode”和“rostopic”查看节点和话题的行为,“rosnode info”输出关于节点本身的信息,”rostopic list”列出所有话题名称,”rostopic info”输出关于话题本身的信息,”rostopic echo”输出话题发布的消息,使用示例如下所示:
rosnode info /talker
rosnode info /listener
rostopic list
rostopic info /chatter
rostopic echo /chatter
例如,”rostopic echo /”
-
rqt_graph
“rqt_graph”用于创建动态图,描述ROS系统正在发生的事情,主要是现有的节点、话题及其发布、订阅关系,也可以看成是信息流。命令示例:
rosrun rqt_graph rgt_graph
结果如下:
- ROS节点名字
ROS节点名字必须唯一,若2次启动相同节点,那么第1次启动的那个节点将会被停止。在启动节点的时候可以更改节点的名字,如下示例演示2次启动talker节点,节点名分别为talker1和talker2:
rosrun chat_pkg chat_pkg talker __name:=talker1
rosrun chat_pkg chat_pkg talker __name:=talker2
1.3.3 roslaunch
“roslaunch”通过运行”launch”文件,同时启动多个节点并配置参数。此外,”roslaunch”将自动启动”roscore”。即不需要像上面那样在3个命令行窗口执行命令,而是配置一个”launch“文件,一次性执行。
语法:
roslaunch PACKAGE LAUNCH_FILE
“launch”文件示例:
如下所示的”launch”文件启动”talker“和”listener”节点,将此内容保存为”chat.launch”文件,放在”~/catkin_ws/src/ros_tutorial/lecture1-3/chat_pkg”目录下。
<launch>
<node name=”talker” pkg=”chat_pkg” type=”talker” output=”screen”/>
<node name=”listener” pkg=”chat_pkg” type=”listener” output=”screen”/>
</launch>
<node>标签中,name为节点的名称,pkg为节点所在的包,type为可执行程序的文件名。注意:output=”screen“使得ROS的日志消息将输出到启动launch文件的命令行窗口。
运行该”launch”文件:
roslaunch chat_pkg chat.launch
结果如下:
1.3.4 速度控制实验
本实验演示在ROS中,通过发布”Twist”类型的消息到”cmd_vel”话题,控制移动机器人的运动速度(机器人订阅了“cmd_vel”话题的数据用于运动控制)。其中,“Twist”类型的消息包含1个3维线速度矢量和1个3维角速度矢量:
geometry_msgs/Vector3 linear
float64 x
float64 y
float64 z
geometry_msgs/Vector3 angular
float64 x
float64 y
float64 z
-
差动驱动机器人
本实验使用了差动轮式机器人由两个独立的驱动轮组成,位于其两侧。当两个轮子向前转动时,机器人向前移动,当一个车轮向前行驶,一个车轮向后行驶时,机器人就地旋转。
差动驱动机器人只能沿其纵轴向前/向后移动,仅绕其垂直轴旋转,即机器人不能横向或垂直移动。因此,我们只需在”Twist”消息中设置线性速度的x分量和角速度的z分量。全向机器人将使用线性速度的Y分量,而飞行或水下机器人将使用所有六个分量。
A Move Turtle Node
通过创建新的ROS包,演示如何操控差动轮式机器人,首先通过如下命令创建”my_turtle”包:
cd ~/catkin_ws/src/ros_tutorial/lecture1-3/
catkin_create_pkg my_turtle std_msgs rospy roscpp
在”~/catkin_ws/src/ros_tutorial/lecture1-3/my_turtle/src/”下创建源文件”move_turtle.cpp”,输入如下内容:
#include “ros/ros.h”
#include “geometry_msgs/Twist.h”
int main(intargc, char **argv)
{
constdoubleFORWARD_SPEED_MPS = 0.5;
//Initialize the node
ros::init(argc, argv, “move_turtle”);
ros::NodeHandlenode;
//A publisher for the movement data
ros::Publisherpub = node.advertise<geometry_msgs::Twist>(“turtle1/cmd_vel”,10);
//Drive forward at a given speed. Therobot points up the x-axis.
//The default constructor will set all commands to 0
geometry_msgs::Twistmsg;
msg.linear.x =FORWARD_SPEED_MPS;
//Loop at 10Hz, publishing movement commands until we shut down
ros::Raterate(10);
ROS_INFO(“Startingto move forward”);
while (ros::ok()){
pub.publish(msg);
rate.sleep();
}
}
在”my_turtle”包中添加”move_turtle.launch”文件:
<launch>
<node name=”turtlesim_node”pkg=”turtlesim” type=”turtlesim_node” />
<node name=”move_turtle” pkg=”my_turtle” type=”move_turtle” output=”screen” />
</launch>
通过如下命令编译、运行launch文件:
cd ~/catkin_ws/
catkin_make
roslaunch mu_turtle move_turtle.launch
顺利的话,你应该看到模拟器里的乌龟不断向前移动直到撞到墙上。
输出乌龟的位姿
为了输出乌龟的位姿,我们需要订阅”/turtle1/pose”话题,通过如下指令,可以获得话题的消息类型和结构:
rostopic type /turtle1/pose| rosmsg show
输出类似如下的结果:
“turtlesim/Pose”的消息类型在”turtlesim”包中定义,因此,在我们的代码中应包含头文件”turtlesim/Pose.h”
#include “ros/ros.h”
#include “geometry_msgs/Twist.h”
#include “turtlesim/Pose.h”
// Topic messages callback
voidposeCallback(constturtlesim::PoseConstPtr&msg)
{
ROS_INFO(“x:%.2f, y: %.2f”,msg->x,msg->y);
}
int main(intargc, char **argv)
{
constdoubleFORWARD_SPEED_MPS = 0.5;
//Initialize the node
ros::init(argc, argv, “move_turtle”);
ros::NodeHandlenode;
//A publisher for the movement data
ros::Publisherpub = node.advertise<geometry_msgs::Twist>(“turtle1/cmd_vel”,10);
//A listener for pose
ros::Subscribersub = node.subscribe(“turtle1/pose”, 10,poseCallback);
//Drive forward at a given speed. Therobot points up the x-axis.
//The default constructor will set all commands to 0
geometry_msgs::Twistmsg;
msg.linear.x =FORWARD_SPEED_MPS;
//Loop at 10Hz, publishing movement commands until we shut down
ros::Raterate(10);
ROS_INFO(“Startingto move forward”);
while (ros::ok()){
pub.publish(msg);
ros::spinOnce();//Allow processing of incoming messages
rate.sleep();
}
}
编译并运行程序:
cd ~/catkin_ws/
catkin_make
roslaunch my_turtle move_turtle.launch
结果:
在launch文件可以使用”args”属性将参数传递给节点,此处,将乌龟的名称作为参数传递给”move_turtle”:
<launch>
<node name=”turtlesim_node”pkg=”turtlesim” type=”turtlesim_node” />
<node name=”move_turtle” pkg=”my_turtle” type=”move_turtle”args=”turtle1″output=”screen”/>
</launch>
在代码中如何使用该参数?示例如下:
int main(intargc, char **argv)
{
constdoubleFORWARD_SPEED_MPS = 0.5;
string robot_name =string(argv[1]);
//Initialize the node
ros::init(argc, argv, “move_turtle”);
ros::NodeHandlenode;
//A publisher for the movement data
ros::Publisherpub = node.advertise<geometry_msgs::Twist>(robot_name + “/cmd_vel”,10);
//A listener for pose
ros::Subscribersub = node.subscribe(robot_name + “/pose”, 10,poseCallback);
geometry_msgs::Twistmsg;
msg.linear.x =FORWARD_SPEED_MPS;
ros::Raterate(10);
ROS_INFO(“Startingto move forward”);
while (ros::ok()){
pub.publish(msg);
ros::spinOnce(); //Allow processing of incoming messages
rate.sleep();
}
}
1.3.5 自定义消息类型
最近比较忙,下次再更吧。
转自公众号:
Robot404