工作空间是一个存放工程开发相关文件的文件夹。所有文件放到一个文件系统下,ROS开发所有工程,称为工作空间。
一、创建工作空间
workspace工作空间:
文件夹:
src: 代码空间(所有工程包源码)
build: 编译空间(编译产生的中间文件)
devel:开发空间(较常用,编译完成后所生成的可执行文件)
install:安装空间(与devel作用基本相同在ros2中合成了一个文件夹)
工作空间结构:
创建工作空间:
编译工作空间:
编译后无措,基于c_make开发的封装ros用。不会编译源码,因为什么都没有添加,可以在哥哥文件夹下查看。
设置环境变量:
很重要,之后找不到安装包可能就是环境白能量没有安装好,找不到节点等问题。终端有zsh和bash,根据自己用的设置后缀。在终端设置后,环境变量只在本终端有效,换个终端之后就无效了,建议在home根目录下改。命令放到根目录下,
vi ~/.bashrc
在最后一行中添加:
source ~/catkin_ws/devel/setup.bash
按shift+wq!(小写与大写指令不同) 保存退出。
然后:
source ~/.bashrc
使设置生效。在打开一个终端查看环境变量即可发现环境变量已经存在。
以下为只有当前终端有环境变量。
检查环境变量:
到此工作空间就建立好了。
创建功能包
指令:
catkin_create_pkg <pakage_name> [depend1] [depend2] [depend3]
创建功能包:
cd ~/catkin_ws/src
catkin_create_pkg learning_communication std_msgs rospy roscpp
出现一系列创建就成功了。rospy :python依赖。 roscpp: c++依赖。
在catkin_ws/src下就已经成功创建了功能包,打开后会有一些文件和文件夹。
src会放置功能包代码,include放置功能包头文件。
package.xlm为功能包的一些描述,具体信息,其中有功能包的一些依赖。功能包依赖。
编译功能包:
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash
编译后会发现新建的功能包。
在src目录下:同一个工作空间下,不允许存在同名功能包;不同工作空间下允许存在同名功能包。
当同一个系统中存在多个同名功能包时ROS的会如何运行,一什么样的顺序去运行,会优先选择那个工作空间中的功能包。
ROS工作空间的Overlaying机制,即工作空间的覆盖。
env:查看系统的环境变量。
env | grep ros :过滤出与ros相关的环境变量。
在锁有环境变量中,其中ROS_PACKAGE_PATH=[路径1][路径2]
ros搜寻功能包的路径会由1向2,依次像后找,如果都找不到系统会报错。
设置的新路径会优先放置到最前端。
测试:
安装一个安装包,由于我用的是ROS Melodic,所以安装ros-melodic-roscpp-tutorials
sudo apt-get install ros-melodic-roscpp-tutorials
如果没安装过就显示安装了。
打开终端使用指令:
rospack find roscpp_tutorials
就会找到安装包路径。
在自己的工作空间新建一个同名的软件包roscpp_tutorials
source ~/.bashrc
使环境变量生效(如果是复制的,需要这样设置)
然后在打开查找安装包路径,就会变为自己所用工作空间的安装包。
二、ROS通信编程
话题编程、服务编程、动作编程
话题通讯模型:
话题编程流程:
一、创建发布者。
小知识:
创建文本文件,在ubuntu18中可能右键没有文本,因此需要我们手动添加。打开模板文档在其中打开终端输入:
sudo gedit txt
会出现一个文本,点保存即可。
如何定义话题消息--话题编程
(1)定义msg文件
(2)在package.xml添加功能包依赖
1 <build_depend>message_generation</build_depend>
2
3 <exec_depend>message_runtime</exec_depend>
(3)在CMakeList.txt添加编译选项
1 find_package(……message_generation)
2
3 catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs message_runtime)
4
5 add_message_files(FILES Person.msg)
6
7 generate_mesages(DEPENDENCIES std_msgs)
学习代码讲解实例:
1 /**
2 * 该例程将发布chatter话题,消息类型String
3 */
4
5 #include <sstream>
6 #include "ros/ros.h"
7 #include "std_msgs/String.h"
8
9 int main(int argc, char **argv)
10 {
11 // ROS节点初始化 节点名称(运行名称)
12 ros::init(argc, argv, "talker");
13
14 // 创建节点句柄
15 ros::NodeHandle n;
16
17 // 创建一个Publisher发布者,发布名为chatter(话题名)的topic,消息类型为std_msgs::String
18 ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
19
20 // 设置循环的频率 多久循环一次
21 ros::Rate loop_rate(10);
22
23 int count = 0;
24 while (ros::ok())
25 {
26 // 初始化std_msgs::String类型的消息
27 std_msgs::String msg;
28 std::stringstream ss;
29 ss << "hello world " << count;
30 msg.data = ss.str();
31
32 // 发布消息
33 ROS_INFO("%s", msg.data.c_str());
34 chatter_pub.publish(msg);
35
36 // 循环等待回调函数 在这里实际上没有意义后边还会讲
37 ros::spinOnce();
38
39 // 按照循环频率延时 不写sleep系统会一直循环,cpu占用会很高
40 loop_rate.sleep();
41 ++count;
42 }
43
44 return 0;
45 }
二、创建订阅者。(简单一些)
源码讲解:
1 /**
2 * 该例程将订阅chatter话题,消息类型String
3 */
4
5 #include "ros/ros.h"
6 #include "std_msgs/String.h"
7
8 // 接收到订阅的消息后,会进入消息回调函数
9 void chatterCallback(const std_msgs::String::ConstPtr& msg)
10 {
11 // 将接收到的消息打印出来
12 ROS_INFO("I heard: [%s]", msg->data.c_str());
13 }
14
15 int main(int argc, char **argv)
16 {
17 // 初始化ROS节点
18 ros::init(argc, argv, "listener");
19
20 // 创建节点句柄
21 ros::NodeHandle n;
22
23 // 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback 需要订阅的话题名为chatter,进入回调函数
24 ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
25
26 // 循环等待回调函数 一直等待消息发布,接收到后会进入回调函数
27 ros::spin();
28
29 return 0;
30 }
(1)添加编译选项。(python就不用设置了)
在CmakeList.text中添加编译文件
add_dependencies是添加自己的第三方依赖。
1 add_executable(talker src/talker.cpp)
2 target_link_libraries(talker ${catkin_LIBRARIES})
3 #add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)
4
5 add_executable(listener src/listener.cpp)
6 target_link_libraries(listener ${catkin_LIBRARIES})
7 #add_dependencies(listener ${PROJECT_NAME}_generate_messages_cpp)
编译完成之后会产生可执行文件,在工作空间catkin_ws/devel/lib下会产生自己的功能包的可执行文件。
(2)运行可执行程序。
—》运行:(在三个终端)
1 roscore
2 rosrun learning_communication talker
3 rosrun learning_communication listener
话题编程
(自定义话题消息)
(1)定义msg文件;(创建)
(2)在package.xml中添加功能依赖(最后)indigo 或更老的版本exec改为 run
1 <build_depend>message_generation</build_depend>
2 <exec_depend>message_runtime</exec_depend>
(3)在CMakeList.txt中添加编译选项
可以使用以下代码来查看自己的消息结构是否定义成功
1 rosmsg show Person
服务编程
(1)定义srv文件
(2)在package.xml中添加功能包依赖
(3)在CMakeList.txt中添加编译选项跟之前的一样
设置编译:
如何实现一个服务器:
服务端代码示例:
1 /**
2 * AddTwoInts Server
3 */
4
5 #include "ros/ros.h"
6 #include "learning_communication/AddTwoInts.h"
7
8 // service回调函数,输入参数req,输出参数res 三个---以上的是request 以下的是response
9 bool add(learning_communication::AddTwoInts::Request &req,
10 learning_communication::AddTwoInts::Response &res)
11 {
12 // 将输入参数中的请求数据相加,结果放到应答变量中
13 res.sum = req.a + req.b;
14 ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
15 ROS_INFO("sending back response: [%ld]", (long int)res.sum);
16
17 return true;
18 }
19
20 int main(int argc, char **argv)
21 {
22 // ROS节点初始化
23 ros::init(argc, argv, "add_two_ints_server");
24
25 // 创建节点句柄
26 ros::NodeHandle n;
27
28 // 创建一个名为add_two_ints的server,注册回调函数add() 服务名 add为回调函数
29 ros::ServiceServer service = n.advertiseService("add_two_ints", add);
30
31 // 循环等待回调函数
32 ROS_INFO("Ready to add two ints.");
33 ros::spin();
34
35 return 0;
36 }
1 /**
2 * AddTwoInts Client
3 */
4
5 #include <cstdlib>
6 #include "ros/ros.h"
7 #include "learning_communication/AddTwoInts.h"
8
9 int main(int argc, char **argv)
10 {
11 // ROS节点初始化
12 ros::init(argc, argv, "add_two_ints_client");
13
14 // 从终端命令行获取两个加数
15 if (argc != 3)
16 {
17 ROS_INFO("usage: add_two_ints_client X Y");
18 return 1;
19 }
20
21 // 创建节点句柄
22 ros::NodeHandle n;
23
24 // 创建一个client,请求add_two_int service,service消息类型是learning_communication::AddTwoInts
25 ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");
26
27 // 创建learning_communication::AddTwoInts类型的service消息
28 learning_communication::AddTwoInts srv;
29 srv.request.a = atoll(argv[1]);
30 srv.request.b = atoll(argv[2]);
31
32 // 发布service请求,等待加法运算的应答结果 调用call如果服务端没有回应,会卡在这儿,发生阻塞
33 if (client.call(srv))
34 {
35 ROS_INFO("Sum: %ld", (long int)srv.response.sum);
36 }
37 else
38 {
39 ROS_ERROR("Failed to call service add_two_ints");
40 return 1;
41 }
42
43 return 0;
44 }
动作编程
通讯机制
(1)定义action文件
(2)在package.xml中添加功能包依赖
(3)在CMakeList.txt中添加编译选项跟之前的一样
加编译代码:
程序示例:
1 #include <ros/ros.h>
2 #include <actionlib/server/simple_action_server.h>
3 #include "learning_communication/DoDishesAction.h"
4
5 typedef actionlib::SimpleActionServer<learning_communication::DoDishesAction> Server;
6
7 // 收到action的goal后调用该回调函数
8 void execute(const learning_communication::DoDishesGoalConstPtr& goal, Server* as)
9 {
10 ros::Rate r(1);
11 learning_communication::DoDishesFeedback feedback;
12
13 ROS_INFO("Dishwasher %d is working.", goal->dishwasher_id);
14
15 // 假设洗盘子的进度,并且按照1hz的频率发布进度feedback
16 for(int i=1; i<=10; i++)
17 {
18 feedback.percent_complete = i * 10;
19 as->publishFeedback(feedback);
20 r.sleep();
21 }
22
23 // 当action完成后,向客户端返回结果
24 ROS_INFO("Dishwasher %d finish working.", goal->dishwasher_id);
25 as->setSucceeded();
26 }
27
28 int main(int argc, char** argv)
29 {
30 ros::init(argc, argv, "do_dishes_server");
31 ros::NodeHandle n;
32
33 // 定义一个服务器
34 Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false);
35
36 // 服务器开始运行
37 server.start();
38
39 ros::spin();
40
41 return 0;
42 }
1 #include <actionlib/client/simple_action_client.h>
2 #include "learning_communication/DoDishesAction.h"
3
4 typedef actionlib::SimpleActionClient<learning_communication::DoDishesAction> Client;
5
6 // 当action完成后会调用该回调函数一次
7 void doneCb(const actionlib::SimpleClientGoalState& state,
8 const learning_communication::DoDishesResultConstPtr& result)
9 {
10 ROS_INFO("Yay! The dishes are now clean");
11 ros::shutdown();
12 }
13
14 // 当action激活后会调用该回调函数一次
15 void activeCb()
16 {
17 ROS_INFO("Goal just went active");
18 }
19
20 // 收到feedback后调用该回调函数
21 void feedbackCb(const learning_communication::DoDishesFeedbackConstPtr& feedback)
22 {
23 ROS_INFO(" percent_complete : %f ", feedback->percent_complete);
24 }
25
26 int main(int argc, char** argv)
27 {
28 ros::init(argc, argv, "do_dishes_client");
29
30 // 定义一个客户端
31 Client client("do_dishes", true);
32
33 // 等待服务器端
34 ROS_INFO("Waiting for action server to start.");
35 client.waitForServer();
36 ROS_INFO("Action server started, sending goal.");
37
38 // 创建一个action的goal
39 learning_communication::DoDishesGoal goal;
40 goal.dishwasher_id = 1;
41
42 // 发送action的goal给服务器端,并且设置回调函数
43 client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
44
45 ros::spin();
46
47 return 0;
48 }
三、实现分布式通信
分布式松耦合
(1)首先确定对方ip地址指令
ifconfig #查看
(2)设置相互通信的IP地址
在主机终端输入:
sudo vi /etc/hosts
添加从机IP地址:
192.168.43.110 alex
在从机添加主机IP:
192.168.43.120 alex-1
设置完成后分别在主机与从机检查是否通信成功:
ping alex #在主机ping从机
ping alex-1 #在从机ping主机
如果都能返回数据说明通信成功。
(3)设置ROS_MASTER_URI (确定哪个是主机,让从机能找到master)11311是rosmaster的一个端口号,必须是这个。
vi ~/.bashrc
export ROS_MASTER_URI=http://alex:11311 #设置环境变量
注意:有些设置好之后可以ping成功但是还是不能用是因为你设置的不是计算机名字,可以在设置中设备详细信息查看但钱计算机的名称。
4、ROS中的关键组件
(1)launch文件
实现多个节点的启动。能自动启动roscore。
output:节点日志信息是否打印
respawn:节点失效失败会重新启动
required:如果这个节点是一个必须的节点,没有的话launch文件启动不冷
ns:命名空间
args:输入参数
1 <launch>
2 <!-- 海龟仿真器 -->
3 <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
4
5 <!-- 键盘控制 -->
6 <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
7
8 <!-- 两只海龟的tf广播 -->
9 <node pkg="learning_tf" type="turtle_tf_broadcaster"
10 args="/turtle1" name="turtle1_tf_broadcaster" />
11 <node pkg="learning_tf" type="turtle_tf_broadcaster"
12 args="/turtle2" name="turtle2_tf_broadcaster" />
13
14 <!-- 监听tf广播,并且控制turtle2移动 -->
15 <node pkg="learning_tf" type="turtle_tf_listener"
16 name="listener" />
17
18 </launch>
(2)TF坐标变换
示例代码:
1 #include <ros/ros.h>
2 #include <tf/transform_broadcaster.h>
3 #include <turtlesim/Pose.h>
4
5 std::string turtle_name;
6
7 void poseCallback(const turtlesim::PoseConstPtr& msg)
8 {
9 // tf广播器
10 static tf::TransformBroadcaster br;
11
12 // 根据乌龟当前的位姿,设置相对于世界坐标系的坐标变换
13 tf::Transform transform;
14 transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
15 tf::Quaternion q;
16 q.setRPY(0, 0, msg->theta);
17 transform.setRotation(q);
18
19 // 发布坐标变换
20 br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
21 }
22
23 int main(int argc, char** argv)
24 {
25 // 初始化节点
26 ros::init(argc, argv, "my_tf_broadcaster");
27 if (argc != 2)
28 {
29 ROS_ERROR("need turtle name as argument");
30 return -1;
31 };
32 turtle_name = argv[1];
33
34 // 订阅乌龟的pose信息
35 ros::NodeHandle node;
36 ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);
37
38 ros::spin();
39
40 return 0;
41 };
1 #include <ros/ros.h>
2 #include <tf/transform_listener.h>
3 #include <geometry_msgs/Twist.h>
4 #include <turtlesim/Spawn.h>
5
6 int main(int argc, char** argv)
7 {
8 // 初始化节点
9 ros::init(argc, argv, "my_tf_listener");
10
11 ros::NodeHandle node;
12
13 // 通过服务调用,产生第二只乌龟turtle2
14 ros::service::waitForService("spawn");
15 ros::ServiceClient add_turtle =
16 node.serviceClient<turtlesim::Spawn>("spawn");
17 turtlesim::Spawn srv;
18 add_turtle.call(srv);
19
20 // 定义turtle2的速度控制发布器
21 ros::Publisher turtle_vel =
22 node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);
23
24 // tf监听器
25 tf::TransformListener listener;
26
27 ros::Rate rate(10.0);
28 while (node.ok())
29 {
30 tf::StampedTransform transform;
31 try
32 {
33 // 查找turtle2与turtle1的坐标变换
34 listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));
35 listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
36 }
37 catch (tf::TransformException &ex)
38 {
39 ROS_ERROR("%s",ex.what());
40 ros::Duration(1.0).sleep();
41 continue;
42 }
43
44 // 根据turtle1和turtle2之间的坐标变换,计算turtle2需要运动的线速度和角速度
45 // 并发布速度控制指令,使turtle2向turtle1移动
46 geometry_msgs::Twist vel_msg;
47 vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
48 transform.getOrigin().x());
49 vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
50 pow(transform.getOrigin().y(), 2));
51 turtle_vel.publish(vel_msg);
52
53 rate.sleep();
54 }
55 return 0;
56 };
(3)Qt工具箱
日志信息可视化工具
rqt_console :节点发出的警告错误鞥节点信息
rqt_graph : 显示节点图
rqt_plot : 画出图,变量的图,数据绘图
rqt_reconfigure : 参数动态配置工具,数据会保存在master,其他节点会查。在这里边改了参数,整个节点都会更新。
(4)Rviz可视化平台
基于rqt开发的,用来显示数据的平台。可视化显示。三维可视化工具。
1 rosrun rviz rviz
在add中添加自己想要监控的数据。
(5)Gazeb物理仿真环境
三位物理仿真平台,开源且免费。带有物理特性。
启动空世界gazebo环境。直接gazebo也可以打开
1 roslaunch gazebo_ros empty_word.launch
扩展资料:
__EOF__
评论(0)
您还未登录,请登录后发表或查看评论