目录
一. 什么是工作空间:
我们开发时会把所有的代码文件、解释文档等各种东西放在一个文件夹下,这个存放工程开发所涉及到的文件夹可以称作是一个工作空间。
二. 如何创建完整的工作空间:
- 创建工作空间文件夹
进入到任意准备放工作空间的文件夹界面,右键点击打开终端
创建工作空间文件夹(文件夹名称:catkin_ws):
mkdir catkin_ws
进入工作空间文件夹:
cd catkin_ws
创建代码空间文件夹(代码空间文件夹名称:src)
mkdir src
- 将文件夹初始化为ROS的工作空间文件夹
进入到代码空间src文件夹:
cd src
初始化工作空间:
catkin_init_workspace
初始化后在src文件夹下会生成一个CMakeLists.txt的文件
- 编译这个工作空间
回到工作空间的根文件夹(catkin_ws)下:
cd ..
编译工作空间:
catkin_make
编译后会出现编译空间build文件夹和开发空间devel文件夹
- 设置环境变量
为了让linux系统能够找到功能包,需要设置环境变量,让系统知道功能包的位置:
source devel/setup.zsh
但是这样设置只是在当前打开的终端内有用,再另外打开终端后会失效,所以最好在终端的配置文件中加载这个命令,这样就可以在进入其他终端环境,系统依旧能够功能包的相对位置。
进入配置文件:
vi ~/.bashrc
按下键Insert进入编辑模式
在最下边添加:
source ~/catkin_ws/devel/setup.bash
按下键Esc,再直接输入:
, 从而到文件们最后一行
在写入 x
, 按下键Enter,退出vi模式
再重新运行以下配置文件:
source ~/.bashrc
如果出错,再vi进入配置文件看看里边是不是多写了什么东西。
检查是否配置好环境变量了:
echo $ROS_PACKAGE_PATH
三. 如何创建功能包:
- 进入代码空间src文件夹:
cd src
创建功能包:
格式:catkin_create_pkg package_name depend1 depend2 depend3 |
---|
例如创建功能包名称learning,依赖于ros的标准信息格式,c++,python:
catkin_create_pkg learning std_msgs rospy roscpp
这时候在src文件夹中就出现了learning功能包的文件夹,打开里边四项内容:
include:功能包需要的一些头文件
src:功能报内的相关源码文件
CMakeLists.txt:放置我们如何去编译这个功能包的一些编译选项,比方我们现在写了一个c++的程序文件,我们必须要在这个文件中添加一些语句,使得在编译时能够将我们的代码文件生成可执行文件。另外里边还包含了我们所依赖的一些package,都在里边有所编译。
package.xml:描述这个功能包的具体信息,功能包的名字,版本号,列出依赖那些其他的功能包
注意事项:在同一个工作空间下不能创建两个名字相同的功能包。
- 编译这个功能包:
回到工作空间根目录下:
cd ..
再编译工作空间:
catkin_make
在编译的信息当中我们可以看到我们刚才已经创建好的功能包:
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~ traversing 1 packages in topological order:
-- ~~ - learning
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 关于环境变量的查找
–工作空间的路径依次会存储到ROS_PACKAGE_PATH中
–新创建的环境变量都会放置在所有环境变量的最前端
–查找时,ROS会优先从前边往后开始查找环境变量
四. 如何创建话题
- 创建话题的发布者
在catkin_ws /src /learning /src 文件夹下创建c++的程序文件,这里命名为talker.cpp
在该文件夹下右键打开终端:
touch talker.cpp
再打开在里边添加程序:
//发布话题:chatter 消息类型:String
#include<sstream> //字符串类的头文件
#include<ros/ros.h>
#include "std_msgs/String.h" //ROS字符串型消息的头文件
int main(int argc, char **argv)
{
//ROS节点初始化,节点的名称就是talker
ros::init(argc,argv,"talker");
//创建节点句柄
ros::NodeHandle n;
//创建一个publisher,发布名为chatter的topic,消息的类型为std_msgs::String
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类型的信息
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;
}
- 创建话题的订阅者
在catkin_ws /src /learning /src 文件夹下创建c++的程序文件,这里命名为listener.cpp
在该文件夹下右键打开终端:
touch listener.cpp
再打开在里边添加程序:
//订阅chatter话题,消息类型:String
#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节点,节点名称listener
ros::init(argc,argv,"listener");
//创建节点句柄
ros::NodeHandle n;
//创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback
ros::Subscriber sub = n.subscribe("chatter",1000,chatterCallback);
//循环等待回调函数
ros::spin();
return 0;
}
- 编译代码
打开catkin_ws /src /learning /src 文件夹的CMakeLists.txt文件,在其中添加:
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_executable是为了将代码生成执行的文件 |
---|
格式:add_executable(project_name src/program_name.cpp ) |
回到catkin_ws文件夹下的终端进行编译:
catkin_make
如果编译有错误的话可能会是代码错误,比方说字母的大小写等都会编译不成功。
与我们设置的是一样的。
五. 如何自定义话题消息类型
- 定义msg文件
回到功能包learning的文件夹中,打开终端,新建一个msg文件夹,一般自定义话题消息都会放在这个文件夹下,约定俗成的。
mkdir msg
在msg文件夹下再新建一个msg文件,命名Person
cd msg
touch Person.msg
在里边添加自己想要定义的消息类型:
string name
uint8 sex
uint8 age
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
- 添加功能包依赖:
打开package.xml文件,加入代码:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
- 添加编译选项
打开CmakeLists.txt文件,找到find_package语句,在里边添加:
message_generation
添加后如下:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
找到catkin_package语句,修改成这个样子(就是去掉那一行注视符,添加了一个message_runtime):
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES learning
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
再在文件中添加语句:
add_message_files(
FILES
Person.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
回到catkin_ws的终端进行编译:
catkin_make
如果有错误,再回头看是不是哪一个步骤有错误,正常来说会正常编译。
查看msg话题消息是否自定义成功:
rosmsg show Person
显示信息:
liuhuan@liuhuan-G5-5587:~/catkin_ws$ rosmsg show Person
[learning/Person]:
uint8 unknown=0
uint8 male=1
uint8 female=2
string name
uint8 sex
uint8 age
表明自定义成功。
六. 如何自定义服务请求和应答
- 定义srv文件
回到功能包learning的文件夹中,打开终端,新建一个srv文件夹,一般自定义服务请求和应答都会放在这个文件夹下,约定俗成的。
mkdir srv
在srv文件夹下再新建一个srv文件,命名MathAdd
cd srv
touch MathAdd.srv
在里边添加自己想要自定义的服务请求和应答类型:
int64 a
int64 b
---
int64 sum
三个横线把上下分为两大部分,上边是请求数据,下边是应答数据
- 添加功能包依赖:
打开package.xml文件,加入代码(和自定义话题是一样的):
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
- 添加编译选项
打开CmakeLists.txt文件,找到find_package语句,在里边添加(和自定义话题是一样的):
message_generation
添加后如下:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
找到catkin_package语句,修改成这个样子(就是去掉那一行注视符,添加了一个message_runtime)(和自定义话题是一样的):
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES learning
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
再在文件中添加语句:
add_service_files(
FILES
MathAdd.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
注意事项:add_service_files 一定要在 generate_messages的前边,如果既有自定义话题,又有自定义的服务,一定要注意这一点。
回到catkin_ws的终端进行编译:
catkin_make
如果有错误,再回头看是不是哪一个步骤有错误,正常来说会正常编译。
查看msg话题消息是否自定义成功:
rossrv show MathAdd
显示信息:
liuhuan@liuhuan-G5-5587:~/catkin_ws$ rossrv show MathAdd
[learning/MathAdd]:
int64 a
int64 b
---
int64 sum
表明自定义成功。
七. 如何创建服务
- 创建服务器:
在catkin_ws /src /learning /src 文件夹里边创建MathAddServer的c++文件
在该文件夹下打开终端:
touch MathAddServer.cpp
在里边添加代码:
/**
* MathAddServer
*/
#include "ros/ros.h"
#include "learning/MathAdd.h"
// service回调函数,输入参数req,输出参数res
bool add(learning::MathAdd::Request &req,
learning::MathAdd::Response &res)
{
// 将输入参数中的请求数据相加,结果放到应答变量中
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节点初始化
ros::init(argc, argv, "add_two_ints_server");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个名为add_two_ints的server,注册回调函数add()
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
// 循环等待回调函数
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
- 创建客户端:
在catkin_ws /src /learning /src 文件夹里边创建MathAddClient的c++文件
在该文件夹下打开终端:
touch MathAddClient.cpp
在里边添加代码:
/**
* MathAddClient
*/
#include <cstdlib>
#include "ros/ros.h"
#include "learning/MathAdd.h"
int main(int argc, char **argv)
{
// ROS节点初始化
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;
// 创建一个client,请求add_two_int service,service消息类型是learning::MathAdd
ros::ServiceClient client = n.serviceClient<learning::MathAdd>("add_two_ints");
// 创建learning::MathAdd类型的service消息
learning::MathAdd 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;
}
- 编译代码
打开catkin_ws /src /learning /src 文件夹的CMakeLists.txt文件,在其中添加:
add_executable(MathAddServer src/MathAddServer.cpp)
target_link_libraries(MathAddServer ${catkin_LIBRARIES})
add_dependencies(MathAddServer ${PROJECT_NAME}_generate_messages_cpp)
add_executable(MathAddClient src/MathAddClient.cpp)
target_link_libraries(MathAddClient ${catkin_LIBRARIES})
add_dependencies(MathAddClient ${PROJECT_NAME}_generate_messages_cpp)
可以在catkin_ws /devel /lib /learning中看到生成的可执行文件。
回到原来catkin_ws的终端进行编译:
catkin_make
- 进行MathAddServer和MathAddClient的通信
新建终端,启动节点管理器:
roscore
新建终端,运行MathAddServer:
rosrun learning MathAddServer
再新建终端,运行MathAddClient:
rosrun learning MathAddClient 1 2
可以看到实现的两个数字的加法。
八. 什么是动作编程
- 什么是动作(action):
一种具有连续反馈的问答通信机制,比如我们在做机器人时候,机器人能够实时回复自身的状态 - 动作的接口:
九. 如何自定义动作消息:
- 定义action文件
回到功能包learning的文件夹中,打开终端,新建一个action文件夹,一般自定义服务请求和应答都会放在这个文件夹下,约定俗成的。
mkdir action
在srv文件夹下再新建一个action文件,命名DoDishes
cd srv
touch DoDishes.action
在里边添加自己想要自定义的动作消息类型:
# define the goal
uint32 dishwasher_id
---
# define the result
uint32 total_dishes_cleaned
---
# define a feedback message
float32 percent_complete
两条横线把上下分为三大部分,上边是定义目标数据,中间是结果,下边是反馈
- 添加功能包依赖:
打开package.xml文件,加入代码(和自定义话题是一样的):
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
- 添加编译选项
打开CmakeLists.txt文件,找到find_package语句,在里边添加(和自定义话题是一样的):
actionlib
actionlib_msgs
添加后如下:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
actionlib
actionlib_msgs
)
再在文件中添加语句(必须在generate_messages语句的前边):
add_action_files(
FILES
DoDishes.action
)
找到generate_messages语句,在里边添加
actionlib_msgs
添加好后是这个样子的:
generate_messages(
DEPENDENCIES
std_msgs
actionlib_msgs
)
回到catkin_ws的终端进行编译:
catkin_make
如果有错误,再回头看是不是哪一个步骤有错误,正常来说会正常编译。
查看msg话题消息是否自定义成功:
rossrv show MathAdd
显示信息:
liuhuan@liuhuan-G5-5587:~/catkin_ws$ rossrv show MathAdd
[learning/MathAdd]:
int64 a
int64 b
---
int64 sum
表明自定义成功。
十. 如何实现动作编程
- 创建动作服务器:
在catkin_ws /src /learning /src 文件夹里边创建DoDishesServer的c++文件
在该文件夹下打开终端:
touch DoDishesServer.cpp
在里边添加代码:
#include <ros/ros.h>
#include <actionlibrver/simple_action_server.h>
#include "learning/DoDishesAction.h"
typedef actionlib::SimpleActionServer<learning::DoDishesAction> Server;
// 收到action的goal后调用该回调函数
void execute(const learning::DoDishesGoalConstPtr& goal, Server* as)
{
ros::Rate r(1);
learning::DoDishesFeedback feedback;
ROS_INFO("Dishwasher %d is working.", goal->dishwasher_id);
// 假设洗盘子的进度,并且按照1hz的频率发布进度feedback
for(int i=1; i<=10; i++)
{
feedback.percent_complete = i * 10;
as->publishFeedback(feedback);
r.sleep();
}
// 当action完成后,向客户端返回结果
ROS_INFO("Dishwasher %d finish working.", goal->dishwasher_id);
as->setSucceeded();
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "do_dishes_server");
ros::NodeHandle n;
// 定义一个服务器
Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false);
// 服务器开始运行
server.start();
ros::spin();
return 0;
}
- 创建action客户端:
在catkin_ws /src /learning /src 文件夹里边创建DoDishesClient的c++文件
在该文件夹下打开终端:
touch DoDishesClient.cpp
在里边添加代码:
#include <actionlib/client/simple_action_client.h>
#include "learning/DoDishesAction.h"
typedef actionlib::SimpleActionClient<learning::DoDishesAction> Client;
// 当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
const learning::DoDishesResultConstPtr& result)
{
ROS_INFO("Yay! The dishes are now clean");
ros::shutdown();
}
// 当action激活后会调用该回调函数一次
void activeCb()
{
ROS_INFO("Goal just went active");
}
// 收到feedback后调用该回调函数
void feedbackCb(const learning::DoDishesFeedbackConstPtr& feedback)
{
ROS_INFO(" percent_complete : %f ", feedback->percent_complete);
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "do_dishes_client");
// 定义一个客户端
Client client("do_dishes", true);
// 等待服务器端
ROS_INFO("Waiting for action server to start.");
client.waitForServer();
ROS_INFO("Action server started, sending goal.");
// 创建一个action的goal
learning::DoDishesGoal goal;
goal.dishwasher_id = 1;
// 发送action的goal给服务器端,并且设置回调函数
client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
ros::spin();
return 0;
}
- 编译代码
打开catkin_ws /src /learning /src 文件夹的CMakeLists.txt文件,在其中添加:
add_executable(DoDishesServer src/DoDishesServer.cpp)
target_link_libraries(DoDishesServer ${catkin_LIBRARIES})
add_dependencies(DoDishesServer ${PROJECT_NAME}_generate_messages_cpp)
add_executable(DoDishesClient src/DoDishesClient.cpp)
target_link_libraries(DoDishesClient ${catkin_LIBRARIES})
add_dependencies(DoDishesClient ${PROJECT_NAME}_generate_messages_cpp)
可以在catkin_ws /devel /lib /learning中看到生成的可执行文件。
回到原来catkin_ws的终端进行编译:
catkin_make
- 进行DoDishesServer和DoDishesClient的通信
新建终端,启动节点管理器:
roscore
新建终端,运行DoDishesServer:
rosrun learning DoDishesServer
再新建终端,运行DoDishesClient:
rosrun learning DoDishesClient
本文章是自己在学习胡春旭老师资料期间所做的学习笔记,特别感谢胡春旭老师的讲解。本问章中的很多内容还是跟着胡老师的资料内容是一样的,如果有侵权的地方会立即删除。如果上边错误的地方,还请大家多多指导,一起学习交流!