ros的桌面完全安装方式安装的功能包安装在 /opt/ros/kinetic/share 中,比如小乌龟
小乌龟例程(暂时还没找到源代码)
0 典型工作空间包括以下四个目录
- src:代码空间
- build:存放编译中缓存信息和中间文件
- devel:存放编译的可执行文件
- install:编译成功后,可使用make install将可执行文件安装到这里,运行该空间环境变量脚本,即可在终端运行这些可执行文件,一般没这个文件夹,目前还没遇到过
工作空间覆盖
应用背景:这个挺有用的,比如我们在调试机械臂时,有两个工作空间都包括一个功能包Universal robot,那么当你在终端输入roslaunch 想启动其中某个launch文件时,它会启动哪个功能包的呢?
答案:ROS_PACKAGE_PATH会记录ROS工作空间路径,上面会启动排在前面的路径下的launch文件。
这样有个弊端,就是如果两个工作空间内的同名功能包不一样,在使用时一定要确定启动的是哪一个。当然还最好的办法是保证添加到 ~/.bashrc 工作空间之间没有同名功能包
env | grep ros # 查看所有ROS相关环境变量
1 RoboWare 软件(强烈推荐)
- 软件最左侧“Debug”和“ROS”很有用,前者可调式c++和python代码,python调试举例如下图;后者可以显示当前活跃话题、节点等信息
- 远程部署与调试:这个没尝试过,可将本地代码部署到远程机器上
- 新加的cpp、msg、srv等文件,它会直接把CMakeLists.txt和package.xml配置好
![25c32a4587e66a8c8843956b173d6a0f.png](https://i-blog.csdnimg.cn/blog_migrate/6a13b6f11b2bc96c0b2ea90bc1217b5b.jpeg)
2 话题发布与订阅者
rqt_graph
查看节点关系图
ros::init(argc, argv, "节点名称");
- 前两个参数是命令行或launch文件输入的参数,可以用来完成命名重映射
- 节点名称必须独一无二,相同名称不能同时存在
ros::NodeHandle n;
创建节点句柄,方便对节点资源的使用和管理
ros::Rate loop_rate(10); # 循环外定义频率为10Hz
loop_rate.sleep(); # 循环内,按照设定频率休眠
void chatterCallback(const std_msgs::String::ConstPtr& msg)
回调函数是订阅节点接收消息的基础机制,当有消息到达时会自动以消息指针作为参数。强调下,这里是指针,它会根据订阅者创建时的设置,接收订阅的话题
编译功能包
C++是编译语言,运行之前需要将代码编译成可执行文件;python是解析语言,不需要编译。ROS中C++编译用的cmake,编译规则修改CMakeLists.txt即可。
这里用catkin创建功能包,很多编译选项已自动配置。
- include_directories:设置头文件的相对路径。全局路径默认是功能包所在目录
- add_executable:设置需要编译的代码和生成的可执行文件。第一个参数为可执行文件名称;后面参数为参与编译的源文件,有多个则用空格分割列出。
- target_link_libraries:设置链接库。第一参数为可执行文件代码,后面依次列出需要连接的库。
- add_dependencies:设置依赖。可添加其他需要依赖的功能包
自定义消息 msg
ROS中的元功能包 common_msg 中提供了std_msgs(标准数据类型)、geometry_msgs(几何学数据)、sensor_msgs(传感器),但这些不够,所以ROS也提供了一套语言无关消息类型定义方法。定义步骤如下:
- 功能包 msg 文件夹下创建消息文件 Personal.msg,比如
string name
uint8 sex
uint8 age
注:这里 string 和 uint8均是语言无关的
很多ROS消息定义还会包含一个标准格式头信息 std_msgs/Header
uint32 seq
time stamp
string frame_id
- seq是消息的顺序标识,不需手动设置
- stamp是消息中与数据相关联的时间戳,可用于时间同步
- frame_id 是消息中与数据相关联的参考坐标系id
要用创建的消息,还需要编译msg文件,按如下操作
2. 在 package.xml中添加功能包依赖yilai
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
3. CMakeLists.txt中添加编译选项
在find_package中添加消息生成依赖的功能包 message_generation
find_package(catkin REQUIRED COMPONENTS
...
message_generation
)
4. catkin依赖进行以下设置
catkin_package(
......
CATKIN_DEPENDS roscpp ... message_runtime
......)
5. 设置需要编译的msg文件
add_message_files(
FILES
Personal.msg
)
generate_message(
DEPENDENCIES
std_msgs
)
6. catkin_make编译即可
3 服务与客户端
服务是节点之间同步通信的方式,客户端节点发送请求,服务端节点处理后反馈应答。
rosservice有一系列命令,小乌龟例程可以通过调用服务进行新建、重置、清除等操作,比如
rosservice call /spawn '8.0' '8.0' '0.0' 'turtle3' # 在8 8 位置生成一个新的小乌龟
如何自定义服务数据
服务数据放置在srv文件夹,与消息类似,不同的是它包含请求和应答两个数据域,二者之间用“---”分割,这对应服务端与客户端需要的数据类型,比如
int64 a
int64 b
---
int64 sum
要使用服务,需更改package.xml和CMakeLists.txt文件配置依赖和编译规则,参照msg。编译成功后,就可以在服务和客户节点中直接使用服务消息了。
摘抄一下service和client的主要代码部分,主要是为了熟悉使用自定义服务的格式。
service.cpp
// service回调函数,输入参数req,输出参数res
bool add(learning_communication::AddTwoInts::Request &req,
learning_communication::AddTwoInts::Response &res)
{
// 将输入参数中的请求数据相加,结果放到应答变量中
res.sum = req.a + req.b;
return true;
}
// main 函数中关键代码
// 创建一个名为add_two_ints的server,注册回调函数add()
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
回调函数是真正实现服务功能的部分。add()函数用于完成两个变量相加,其传入参数便是我们服务数据类型描述文件中声明的请求与应答数据结构,完成运算后,结果会放到应答数据中,反馈给client。
client.cpp
// 创建一个client,请求 add_two_int service
// service 消息类型是 learning_communication::AddTwoInts
ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");
// 创建 learning_communication::AddTwoInts 类型的service消息
learning_communication::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
// 发布service请求,等待加法运算的应答结果
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;
4 ROS中命名空间
这个还比较重要,因为ros各个部分都涉及命名空间,比如节点、参数、话题、服务等。虽然有命名空间的概念,但是各个空间内的命名也都可以在全局范围内访问。
有效命名
- 首字符是字母(大小写均可)、波浪线~、左斜杠/
- 后续字符可以字母、数字、下划线、左斜杠
命名解析
主要分为一下四种,分别说一下
1)基础名称:如base,可看做相对名称的一个子类,用来描述资源本身。
2)全局名称:首字符是左斜杠,如/global/name。全局名称解析度最高,但应尽量少用,会影响代码可移植性,说白点,就是代码作为模块在别的地方使用,可能还要修改名称。
3)相对名称:由ROS提供默认命名空间,不需左斜杠。比如relative/name解析到全局就是/relative/name。可以看出重要的是如何确定默认命名空间,有三种方法:
- 通过命令参数设置。调用ros::init的ROS程序会接收名为_ _ns的命令行参数,赋值方式为 _ _ns:=default-namespace
- launch文件中设置。比如
<node name="xxx" pkg="xxx" type="xxx" ns="default-namespace" />
- 使用环境变量设置。在执行ROS程序的终端设置默认命名空间的环境变量,应该是只对当前终端有效。试验了一下,没设置对,
export ROS_NAMESPACE=default-namespace
很显然相对名称移植性好,就是可以直接将一个相对命名的节点移植到其他命名空间内。
4) 私有名称:波浪线开头,只在节点内部使用。他的全局名称也需要ROS为其解析,与相对名称使用当前默认命名空间不同,它使用节点的全局名称作为命名空间。
命名重映射
所有ROS节点内的资源都可以在节点启动时进行命名重映射。这样我们即使同时打开多个相同节点,也不会发生命名冲突,重映射语法为
name:=new_name
比如将chatter重映射为 /wg/chatter
rosrun rospy_tutorials talker chatter:=/wg/chatter
由于ROS命名解析是在命名重映射之前发生,所以注意区别两个重映射格式
foo:=bar
/foo:=bar # 这里只能将全局解析为/foo的名称重映射为bar。而/baz/foo还是其本身,无法映射
5 分布式多机通信
这是ROS优点。节点可运行在不同的计算平台,通过 Topic 、 Service通信。因为Master只能运行在一台机器上,其它机器通过ssh的方式和Master联系,使用步骤主要包括:
1)确定所有计算机处于同一网络
2)通过 ifconfig 命令查看计算机局域网的IP地址
3)分别在两台计算机系统 /etc/hosts 文件中加入对方 IP 地址和对应计算机名
4)使用 ping 测试网络通吗
5)设置环境变量 ROS_MASTER_URI,也就是其它机器需要知道Master运行在哪里,在非Master机器上运行如下示例代码:
export ROS_MASTER_URI=http://主机名:11311 # 当前终端有效
echo "上面这串代码" >> ~/.bashrc # 本计算机所有终端有效
??上面的11311是啥意思?
这里有个问题,如果是远程怎么办呢?也就说无法连接到同一个网络