1、创建工作空间
1.1 创建指令
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
1.2 创建编译空间
cd ~/catkin_ws
catkin_make
1.3 设置环境变量
source devel/setup.bash
1.4 检查环境变量
echo $ROS_PACKAGE_PATH
1.5 创建功能包
cd ~/catkin_ws/src
catkin_create_pkg learning_communication std_msgs rospy roscpp
指令中的learning_communication是功能包的名称
1.6 编译功能包
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash
2、自定义话题消息
2.1 在刚才创建的功能包下面再创建一个文件夹,并命名为msg,并在文件夹中创建Person.msg文件
cd ~/catkin_ws/src/learning_communication/
mkdir msg
cd msg
touch Person.msg
2.2 编辑Person.msg文件
双击文件打开Person.msg,并将代码粘贴到文件中
string name
uint8 age
uint8 sex
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
2.3 添加功能包依赖
打开功能包中的package.xml,在里面添加如下代码
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
添加后部分代码如下图:
2.4 在CMakeLists.txt添加编译选项
在里面找到find_package(…),在里面添加上message_generation
再在里面继续添加
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)
然后再里面找到catkin_package,进行修改
添加好之后:
2.5 编译
进入到根目录中,进行编译
cd ../../
catkin_make
编译完成之后我们可以进入到/catkin_ws/include/learning_communication路径下看到编译出来的C++头文件
2.6 创建订阅者和发布者cpp文件
进入到功能包下面的src文件夹下,输入指令创建出两个文件
touch person_publisher.cpp
touch person_subscriber.cpp
publisher中的代码:
#include<ros/ros.h>
#include"learning_communication/Person.h"
int main(int argc, char** argv) {
//ROS节点初始化
ros::init(argc, argv, "person_publisher");
//创建节点句柄
ros::NodeHandle n;
//创建一个publisher,发布名为/fibonacci的topic,消息类型为std_msgs::Int32,队>列长度为10
ros::Publisher person_info_pub = n.advertise<learning_communication::Person>("/person_info", 100);
//设置循环频率
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok()) {
//初始化geometry_sgs::Twist类型消息
learning_communication::Person person_msg;
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = learning_communication::Person::male;
//发布消息
person_info_pub.publish(person_msg);
ROS_INFO("Information: name:%s, age: %d, sex: %d", person_msg.name.c_str(), person_msg.age, person_msg.sex);
//按循环频率延时
loop_rate.sleep();
}
return 0;
}
subscriber中的代码:
#include<ros/ros.h>
#include"learning_communication/Person.h"
void poseCallback(const learning_communication::Person::ConstPtr& msg)
{
//将接受到的消息打印出来
ROS_INFO("Information: name:%s, age: %d, sex: %d", person_msg->name.c_str(), person_msg->age, person_msg->sex);
}
int main(int argc, char** argv)
{
//初始化ROS节点
ros::init(argc, argv, "person_subscriber");
//创建节点句柄
ros::NodeHandle n;
//创建一个Subscriber,订阅名为/fibonacci的topic,注册回调函数poseCallback
ros::Subscriber person_info_sub = n.subscribe("/person_info", 1000, poseCallback);
//循环等待回调函数
ros::spin();
return 0;
}
2.7 进行链接
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
2.8 开启ros,运行代码
先编译一下:
cd catkin_ws
catkin_make
然后开启ros:
roscore
然后开启一个终端,输入指令,开启订阅
rosrun learning_communication person_subscriber
再开启一个终端,输入指令,开启发布:
rosrun learning_communication person_publisher
2.9 遇到问题
在输入‘rosrun learning_communication person_publisher’和‘rosrun learning_communication person_subscriber’指令时,总是会出现:
Error: package ‘learning_communication’ not found的报错,翻译一下就是没有找到功能包。
想要解决这个问题就需要每开启一个终端就需要将环境变量的配置脚本(source ~/catkin_ws/devel/setup.bash)添加到终端的配置文件(.bashrc)中即可解决问题
cd ~/catkin_ws
source devel/setup.bash
输出结果中出现乱码问题可能是因为代码中输入中文的问题,修改一下即可。
3、ROS通信编程——服务编程
要求:编写代码实现 ROS 中的服务请求与答复: 创建服务端,注册 Service,当服务端收到客户端 Service 请求(携带整型参数 a.b) 后,服务端返回 a.b 的和给客户端,客户端输出结果。如,客户端给服务端 Service 发送参数 3,9,服务端返回 12,客户端输出: 12。
3.1 创建srv文件,并编写代码
我们就在上面的功能包下创建一个srv文件夹,将number.srv创建在该文件夹下。并将如下代码编写在文件中:
// 客户端请求时发送的两个数据
int32 num1
int32 num2
// 服务端的结果
int32 sum
3.2 添加依赖
打开package.xml,将代码添加在文件中。由于这一步之前已经做过了,可以直接跳过。
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
3.3 在CMakeLists.txt添加编译选项
打开CMakeLists.txt文件,在里面找到find_package(…),在里面添加上message_generation(如果已经添加过就可以不用添加)
然后再添加:
add_service_files(FILES number.srv)
generate_messages(DEPENDENCIES std_msgs)
在catkin_package(….)结尾添加message_runtime
修改后的代码如图:
已经添加过的就不用再次添加了。
3.4 编译生成头文件
回到工作空间的根目录下编译生成相应的头文件,和2.5的步骤相同,就不再过多赘述
cd ~/catkin_ws
catkin_make
3.5 编写代码
先在srv文件夹下创建出两个文件number_client.cpp和number_server.cpp
number_client.cpp
#include "ros/ros.h"
#include "learning_communication/number.h"
int main(int argc, char **argv)
{
// 初始化 ROS 节点
ros::init(argc, argv, "number_client");
// 创建 ROS 句柄
ros::NodeHandle n;
// 创建 客户端 对象
ros::ServiceClient client = n.serviceClient<learning_communication::number>("number");
// 提交请求并处理响应
learning_communication::number a;
//提交请求
a.request.num1 = 3;
a.request.num2 = 9;
//处理响应
bool flag = client.call(a);
if (flag)
{
ROS_INFO("Successful!sum of two numbers:%d", a.response.sum);
}
else
{
ROS_INFO("Failed!");
}
// ros::spin()循环等待回调函数
ros::spin();
}
number_server.cpp
#include "ros/ros.h"
#include "learning_communication/number.h"
// 回调函数:bool 返回值由于标志是否处理成功
bool Callback(learning_communication::number::Request& req,
learning_communication::number::Response& resp)
{
//1处理请求
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("receviced numbers:num1 = %d, num2 = %d", num1, num2);
//2组织响应
int sum = num1 + num2;
resp.sum = sum;
ROS_INFO("sum of two numbers:sum = %d", sum);
return true;
}
int main(int argc, char **argv)
{
// 初始化 ROS 节点
ros::init(argc, argv, "number_server");
// 创建 ROS 句柄
ros::NodeHandle n;
// 创建 服务 对象
ros::ServiceServer server = n.advertiseService("number", Callback);
ROS_INFO("server start....");
// 回调函数处理请求并产生响应
}
3.6 进行链接
和前面的操作类似,这里也要向CMakeLists.txt文件中添加一些代码
add_executable(number_server srv/number_server.cpp)
target_link_libraries(number_server ${catkin_LIBRARIES})
add_dependencies(number_server ${PROJECT_NAME}_generate_messages_cpp)
add_executable(number_client srv/number_client.cpp)
target_link_libraries(number_client ${catkin_LIBRARIES})
add_dependencies(number_client ${PROJECT_NAME}_generate_messages_cpp)
3.7 启动ros,运行代码
这里的操作和前面的相似,如果遇到了相同的问题还是同样的解决方法。这里就直接粘贴上结果了。其中的乱码应该是因为代码中有中文。
4、总结
本次的试验说简单,但是不是很简单。刚开始的时候没有怎么明白为什么要这样写代码。但是写到后面之后就逐渐的明白了,这些代码相当于是套模板一样的,只要懂了一点,后面的就简单了。