我将ROS的CPP部分分成7个部分:
1、基础的node param
2、动态调节参数
3、关于TF变换
4、actionlib
5、插件技术
6、movebase
7、nodelet技术
前言
相比于ros学习笔记一,ros学习笔记一补充篇将会更加注重代码的讲解。ros学习笔记一只是像大观园一样,看看ROS这个领域的山山水水。而ros学习笔记一补充篇将会深入的补充这些山山水水的来源
节点node
rosrun 使用格式:
rosrun 包名 节点名
rosnode list 将会显示出正在运行的节点,其实我们平时都是直接用rosrun rqt_graph rqt_graph来吧正在运行的节点和话题之间的信息给显示出来
话题
节点node1和节点node2是通过话题来通信,节点node1发布topic1,而节点node2订阅topic1
我们可以输入 rostopic echo 话题1来显示出话题的信息
如何查看一个node到底能够发布多少个话题,用
rostopic list
在这个下面列出来的topic都是可以被订阅的
当然也 可以用
rostopic echo /
然后 不停的按tab回车来显示出来
发布publish
使用rostopic pub可以吧数据发布到某个正在广播的话题上面
1、我们可以用rostopic type 话题名
来查看这个话题的数据类型
2、然后rosmsg show 话题的数据类型名
3、有了1,2,这两个参数我们就可以发布话题了
示例:
rostopic pub -1 话题名称 话题的数据类型 需要改变的参数
rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
编写简单的消息发布器和订阅器 (C++) catkin方式
前提准备:
talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
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);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
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::spin();
return 0;
}
我下面将两种编译方式,看情况而定,反正我都会哈哈~
当然,个人比较倾向于用IDE,但是用终端编译是最准确的
先讲用终端命令行进行编译吧。
这个是个很经典的框架,很值得记忆!!!!
cd Desktop/
mkdir -p dao/src 这里建立一个stack叫做dao
cd huazi/src/
catkin_init_workspace
cd ..
catkin_make
cd src
catkin_create_pkg daodao roscpp rospy std_msgs genmsg 这里建立一个package叫做daodao
然后把talk.cpp文件放到里面
然后在CMakeLists.txt中修改,这里说的是修改,就是把几个分号给去掉,或者说直接把语句添加上
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker dao_generate_messages_cpp)当然这句话不加也是可以的
要说明的一点就是,这里dao_generate_messages_cpp当中的dao就是我的这个stack的名字
然后做完这一切工作之后,就是stack的目录下进行catkin_make
cd ..
catkin_make
然后运行一些这个节点
roscore
source ~/Desktop/dao/devel/setup.bash
rosrun daodao talker
第二种编译方式
打开Roboware
在桌面上创建一个叫dao111的stack
然后新建一个daodao的包,如果你不知道如何新建一个包的话,就请到我的Roboware学习笔记那篇里面有如何新建一个包的过程
然后新建一个talker.cpp的文件,然后输入代码
值得注意的一点就是,这里的find_package 就要自己写了
另外,很重要很重要的框架
就是按照这个样子来建立文件
然后你就可以在这里看到package 和 node 的名字
然后运行
好到这里两种调试方法结束啦~
在这里我想做个总结,就是packagename自己在创建文件夹的时候时候自己定义的,node的名字值在写cpp文件当中自己的定义的,其中cpp的文件名字就是node的名字
代码解析:
框架:
#include"ros/ros.h"
#include"std_msgs/String.h"
int main(int argc,char **argv)
{
ros::init(argc,argv,"节点名")
ros::NodeHandle n;
}
一个很经典的语句:
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
就像是int a=1;
在这句话中返回值数据类型是int
因此在上一条语句中返回值的数据类型是ros::Publisher,调用句柄n的一个advertise的函数,这个函数的返回值的数据类型是<std_msgs/String>
(引用一句很经典的话:告诉master,我们要在chatter上发布一个<std_msgs/String>的数据类型
) 这里chatter就是topic的话题的名称,1000就是按照我的理解是,在1000字符以内吧
ros::Rate loop_rate(10);
loop_rate.sleep();
这里的10表示的是10hz,如果是1的话,就是1Hz,也即是没1s更新一次
ROS_INFO("%s", msg.data.c_str());
ros::spinOnce();这个地方应该是回调机制
chatter_pub.publish(msg);这里向所有订阅chatter(话题)的节点发布消息我觉的可以记住这个框架,实例化两个对象,
std_msgs::String msg;
std::stringstream ss;
ss<<count;
msg.data=ss.str();
ROS_INFO("%s",msg.data.c_str());
小例子1、
我用上面的框架,写了一个自己的小例子,每隔1秒,向终端ros发布1,2,3…
程序:
#include<sstream>
#include"ros/ros.h"
#include"std_msgs/String.h"
int main(int argc, char **argv)
{
ros::init(argc,argv,"Num");
ros::NodeHandle n;
ros::Publisher chatter_pub=n.advertise<std_msgs::String>("getNum",1000);//使用这条语句发布信息
ros::Rate loop_rate(1);
int count=0;
while(ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss<<count;
msg.data=ss.str();
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
}
另外一定要注意大小写。
小例子2
然后下面我想讲的就是吧这个节点发布的消息给了另外一个节点,然后这这个节点打印出来
这是我的目标
效果:
Num.cpp
#include<sstream>
#include"ros/ros.h"
#include"std_msgs/String.h"
int main(int argc, char **argv)
{
ros::init(argc,argv,"Num");
ros::NodeHandle n;
ros::Publisher chatter_pub=n.advertise<std_msgs::String>("getNum",1000);//使用这条语句发布信息
ros::Rate loop_rate(1);
int count=0;
while(ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss<<count;
msg.data=ss.str();
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
}
get.cpp
#include"ros/ros.h"
#include"std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("%s", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc,argv,"get");
ros::NodeHandle n;
ros::Subscriber sub=n.subscribe("getNum",1000, chatterCallback);
ros::spin();
}
代码解析和总结:
这两句命令在在一起记忆:
ros::spinOnce();
ros::spin();
在publish的cpp文件当中
ros::spinOnce();如果没有这个函数的话,就没有回调信息
在subscribe的cpp文件当中
ros::spin();产生回调信息
然后这个框架我真的觉得应该记住
std_msgs::String msg;实例化类型
std::stringstream ss;
ss<<count;将输出数值给了ss
msg.data=ss.str();将ss和msg相互关联,这样的话后面的输出做准备
chatter_pub.publish(msg);向刚刚的msg的话题进行发布
ros::spinOnce();产生对应的回调函数,在getcpp当中哟对应的
loop_rate.sleep();
++count;
这个输出的回调函数应该是要记住的
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("%s", msg->data.c_str());
}
这个在主函数的当中调用机制也是应该熟悉的!
ros::Subscriber sub=n.subscribe("getNum",1000, chatterCallback);
最后别忘记在CMakeLists.txt当中添加2条语句哈。
python的实现
1、发布一个话题
第一种方式,使用终端实现
在工作区间下
cd catkin_ws/src
catkin_create_pkg daodao roscpp rospy std_msgs genmsg
cd daodao
mkdir scripts
cd scripts
vim talker.py
chmod +x talker.py
talker表示的是发布一个话题
往talker.py中写这些
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(10) # 10hz
while 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
然后
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash
rosrun daodao talker.py
直接就运行成功了
第二种编译方式:
使用roboware
同样使用roboware的时候,还是需要安装一些软件的
为了支持python的调试功能,需要安装pylint
sudo apt-get install python-pip
sudo python -m pip install pylint
同时为了获取更好的代码补齐体验,需要安装clang
sudo apt-get install clang
然后把代码写进去,就可以运行了,在这次调试的过程中,我发现直接在调试窗口,更好一些,能够犯得错误更少
意外情况1:
这是因为roscore没开
另外,指的注意的一点是仍要写cmakelists文件,
意外情况2:
这是因为python没有加#!/usr/bin/python
意外情况3:
这是因为每天添加可执行文件chmod +x talker.py
成功运行:
建议吧python的代码放到scripts这个文件夹中 src中放Cpp文件
代码解析:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(10) # 10hz
while 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
#!/usr/bin/python
# -*- coding: UTF-8 -*-
让编译器找到python
import rospy
from std_msgs.msg import String
导入rospy客户端,和std_msg/String 这个数据类型
queue_size表示的对应的大小
pub = rospy.Publisher('chatter', String, queue_size=10)
这个是话题的名称chatter
rospy.init_node('talker')
初始化节点,节点的名字要唯一,并且不能
rate=rospy.Rate(10)
创建rate对象,与sleep()函数结合使用,控制话题消息发布的频率
while not rospy.is_shutdown():
hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
rospy.is_shutdown()函数是什么意思,目前还不知道。
rate.sleep()用于控制发布的频率
rospy.loginfor(str) 函数在屏幕输出信息,我们把这个信息存储在hello_str当中
2、订阅一个话题
#!/usr/bin/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('listener', anonymous=True)
rospy.Subscriber("chatter", String, callback)
rospy.spin()
if __name__ == '__main__':
listener()
最后别忘记给listener.py添加可执行权限chmod +x listener.py
测试可以通过
代码分析
def 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()
定义listener函数,
第一行,初始化一个节点,后面anony=False ,默认参数是不匿名的,但是现在你也可以选择匿名。匿名之后就不会被重复了
第二行是订阅话题的名称,使用回调函数来输出话题的名称
第三行rospy.spin()是保持你的节点一直运行,直到程序关闭。
接下来这篇博客当中,我希望写的是ros cpp剩下的内容,和python的所有的东西。
编写服务器和客户端
到自己的依赖项里面然后
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
添加message_generation
add_message_files(
FILES
Num.msg
# Message2.msg
)
add_service_files(
FILES
# Service1.srv
AddTwoInts.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES mypackage
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
在package.xml里面添加
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
这个地方的文件名就是你当时创建的srv文件的名称
beginner_tutorials/AddTwoInts.h是由编译系统自动根据我们先前创建的srv文件生成的对应该srv文件的头文件。
里面一般就是有一个输入的参数和一个输出的参数,然后
bool add(mypackage::AddTwoInts::Request &req,mypackage::AddTwoInts::Response &res)
{
res.sum=req.a+req.b;
ROS_INFO_STREAM("res.sum="<<res.sum);
return true;
}
在srv当中,我们使用—将request和respon进行分开。
int64 a
int64 b
---
int64 sum
重要的就是这一句,其实就是这个service的名称和回调函数。
ros::ServiceServer service=nh.advertiseService("add_two_ints",add);
这个头文件姑且可以理解为stdio.h
#include <cstdlib>
利用这个头文件,能够让我们输出参数
mypackage::AddTwoInts srv;
srv.request.a=atoll(argv[1]);
srv.request.b=atoll(argv[