视频链接:bilibili 搜
古月居
源代码:关注古月居公众号,回复ROS入门21讲
1 创建工作空间和功能包
1.1 工作空间
-
工作空间(workspace)是一个存放工程开发相关文件的文件夹,包括:
src:代码空间,存放功能包代码、配置文件、launch文件等
build:编译空间,编译过程中产生的中间文件
devel:开发空间,编译生成的可执行文件、库、脚本等
install:安装空间,install指令安装的默认文件夹,包括最终生成的可执行文件 -
创建工作空间
1.创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace //将当前文件夹变成ROS工作空间的属性
2.编译工作空间
cd ~/catkin_ws/ //编译时需要造工作空间根目录相当于此处catkin_ws
catkin_make
3.设置环境变量
source devel/setup.bash //方便系统找到编译生成的可执行文件
4.检查环境变量
echo $ROS_PACKAGE_PATH
1.2 功能包
-
创建功能包
同一个工作空间下,不允许存在同名功能包
不同工作空间下,允许存在同名功能包
catkin_create_pkg<package_name> [depend1][depend2][depend3]
//后面的depend是指编译时需要依赖ROS中其他的功能包名
1.创建功能包
cd ~/catkin_ws/src
catkin_create_pkg test_pkg std_msgs rospy roscpp
2.编译功能包
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash //添加环境变量
创建生成的功能包下有两个文件夹,以及两个功能包中必须存在的文件:
文件夹
:
include用于放置头文件;
src用于放功能包代码;
文件
:
package.xml:包括功能包基本信息,编译和运行功能包所需的依赖信息
cmakelists.txt:描述编译规则以及编译的设置
2 发布者Publisher的编程实现
通过话题的发布与订阅实现小海龟的运动控制
2.1 创建功能包
cd ~/catkin_ws/src
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim
2.2 创建发布者代码(C++)
cpp文件代码放在创建的功能包对应src目录下
2.2.1 代码
/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/
/**
* 该例程将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist,该消息用来控制海龟运动
*/
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
int main(int argc, char **argv)
{
// ROS节点初始化,ROS中节点不能重名
ros::init(argc, argv, "velocity_publisher");
// 创建节点句柄,用来管理ROS相关API资源,
ros::NodeHandle n;
// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
// 设置循环的频率
ros::Rate loop_rate(10);
// while循环实现:封装数据,发布数据,等待至下一次循环
int count = 0;
while (ros::ok())
{
// 初始化geometry_msgs::Twist类型的消息
geometry_msgs::Twist vel_msg;
vel_msg.linear.x = 0.5;
vel_msg.angular.z = 0.2;
// 发布消息
turtle_vel_pub.publish(vel_msg);
ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]",
vel_msg.linear.x, vel_msg.angular.z);
// 按照循环频率延时
loop_rate.sleep();
}
return 0;
}
2.2.2 配置CMakeLists.txt中的编译规则
将下面两句话添加到功能包cmakelist.txt文件中对应的build位置下
1.设置需要编译的代码和生成的可执行文件
add_executable(velocity_publisher src/velocity_publisher.cpp)
// 将src/velocity_publisher.cpp代码编译成可执行文件velocity_publisher
2.将可执行文件链接到ROS相关库
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})
2.2.3 编译并运行发布者
1.编译
cd ~/catkin_ws
catkin_make
2.添加路径
source devel/setup.bash
3.终端1
roscore
4.终端2
rosrun turtlesim turtle_node
5.在catkin_ws目录下打开终端3
rosrun learning_topic velocity_publisher
如果想省略输入source devel/setup.bash指令,则打开【主文件夹】,按下【ctrl+h】显示隐藏文件,打开【.bashrc】文件,将【source /home/yao/catkin_ws/devel/setup.bash】(一定要是完整路径)指令添加到bashrc的末端,保存退出,重新启动终端生效。采用该方法时,上述步骤5的终端可以在任意位置打开。
2.3 创建发布者代码(Python)
创建与功能包下src同级文件夹scripts,py文件代码放于此
2.3.1 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
########################################################################
#### Copyright 2020 GuYueHome (www.guyuehome.com). ###
########################################################################
# 该例程将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist
import rospy
from geometry_msgs.msg import Twist
def velocity_publisher():
# ROS节点初始化
rospy.init_node('velocity_publisher', anonymous=True)
# 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
#设置循环的频率
rate = rospy.Rate(10)
while not rospy.is_shutdown():
# 初始化geometry_msgs::Twist类型的消息
vel_msg = Twist()
vel_msg.linear.x = 0.5
vel_msg.angular.z = 0.2
# 发布消息
turtle_vel_pub.publish(vel_msg)
rospy.loginfo("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]",
vel_msg.linear.x, vel_msg.angular.z)
# 按照循环频率延时
rate.sleep()
if __name__ == '__main__':
try:
velocity_publisher()
except rospy.ROSInterruptException:
pass
设置py文件属性:右击py文件,选择【属性】,点击【权限】,将【允许作为可执行文件】打勾
2.3.2 运行发布者
1.终端1
roscore
2.终端2
rosrun turtlesim turtle_node
3.在catkin_ws文件所在目录下打开终端3
source devel/setup.bash
rosrun learning_topic velocity_publisher.py
3 订阅者Subscriber的编程实现
3.1 创建订阅者代码(C++)
3.1.1 代码
/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/
/**
* 该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
*/
#include <ros/ros.h>
#include "turtlesim/Pose.h"
// 接收到订阅的消息后,会进入消息回调函数,回调函数不嵌套,需高效运行
// msg以一个常指针的形式指向turtlesim::Pose的所有数据内容
void poseCallback(const turtlesim::Pose::ConstPtr& msg)
{
// 将接收到的消息打印出来
ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y);
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "pose_subscriber");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback
// 此处队列为10,相当于是一个缓冲器
// 当订阅者发现有/turtle1/pose话题的数据进来时,调用回调函数做处理
ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);
// 循环等待回调函数
//有消息进来时转到回调函数,无消息时spin自身做死循环
ros::spin();
return 0;
}
3.1.2 配置订阅者代码编译规则
将下面两句话添加到功能包cmakelist.txt文件中对应的build位置下
add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})
编译
1.编译
cd ~/catkin_ws
catkin_make
2.添加路径
source devel/setup.bash
3.终端1
roscore
4.终端2
rosrun turtlesim turtle_node
5.在catkin_ws目录下打开终端3
rosrun learning_topic pose_subscriber
6.在catkin_ws目录下打开终端4
source devel/setup.bash
rosrun learning_topic velocity_publisher
当采用【source devel/setup.bash】添加路径时,每次打开终端时都需要先链接路径,才能索引到功能包
3.2 创建订阅者代码(python)
3.2.1 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
########################################################################
#### Copyright 2020 GuYueHome (www.guyuehome.com). ###
########################################################################
# 该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
import rospy
from turtlesim.msg import Pose
def poseCallback(msg):
rospy.loginfo("Turtle pose: x:%0.6f, y:%0.6f", msg.x, msg.y)
def pose_subscriber():
# ROS节点初始化
rospy.init_node('pose_subscriber', anonymous=True)
# 创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback
rospy.Subscriber("/turtle1/pose", Pose, poseCallback)
# 循环等待回调函数
rospy.spin()
if __name__ == '__main__':
pose_subscriber()
3.2.2 运行订阅者
1.终端1
roscore
2.终端2
rosrun turtlesim turtle_node
3.在catkin_ws文件所在目录下打开终端3
source devel/setup.bash
rosrun learning_topic pose_subscriber.py
4 话题消息的定义与使用
视频第
十二讲第5分钟
4.1 自定义话题消息
- 定义msg文件
# 在learning_topic下新建msg文件夹,文件夹内新建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>
- cmakelist添加编译选项
find_package(... message_generation)
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_mags)
catkin_package(... message_runtime)
- 编译
catkin_make
4.2 创建Publisher和Subscriber(C++)
Publisher代码
/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/
/**
* 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "person_publisher");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);
// 设置循环的频率
ros::Rate loop_rate(1);
int count = 0;
while (ros::ok())
{
// 初始化learning_topic::Person类型的消息
learning_topic::Person person_msg;
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = learning_topic::Person::male;
//采用定义的宏赋值
// 发布消息
person_info_pub.publish(person_msg);
ROS_INFO("Publish Person Info: name:%s age:%d sex:%d",
person_msg.name.c_str(), person_msg.age, person_msg.sex);
// 按照循环频率延时
loop_rate.sleep();
}
return 0;
}
Subscriber代码
/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/
/**
* 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
// 自己定义的头文件
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
// 将接收到的消息打印出来
ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d",
msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "person_subscriber");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
cmakelist.txt添加语句
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)
//用于和自定义生成的头文件进行链接
运行publisher和subscriber
1.打开终端1
roscore
2.catkin_ws下打开终端2
source devel/setup.bash
rosrun learning_topic person_publisher
3.catkin_ws下打开终端3
source devel/setup.bash
rosrun learning_topic person_subscriber
4.3 创建Publisher和Subscriber(Python)
Publisher代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
########################################################################
#### Copyright 2020 GuYueHome (www.guyuehome.com). ###
########################################################################
# 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
import rospy
from learning_topic.msg import Person
def velocity_publisher():
# ROS节点初始化
rospy.init_node('person_publisher', anonymous=True)
# 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
person_info_pub = rospy.Publisher('/person_info', Person, queue_size=10)
#设置循环的频率
rate = rospy.Rate(10)
while not rospy.is_shutdown():
# 初始化learning_topic::Person类型的消息
person_msg = Person()
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = Person.male;
# 发布消息
person_info_pub.publish(person_msg)
rospy.loginfo("Publsh person message[%s, %d, %d]",
person_msg.name, person_msg.age, person_msg.sex)
# 按照循环频率延时
rate.sleep()
if __name__ == '__main__':
try:
velocity_publisher()
except rospy.ROSInterruptException:
pass
Subscriber代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
########################################################################
#### Copyright 2020 GuYueHome (www.guyuehome.com). ###
########################################################################
# 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
import rospy
from learning_topic.msg import Person
def personInfoCallback(msg):
rospy.loginfo("Subcribe Person Info: name:%s age:%d sex:%d",
msg.name, msg.age, msg.sex)
def person_subscriber():
# ROS节点初始化
rospy.init_node('person_subscriber', anonymous=True)
# 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
rospy.Subscriber("/person_info", Person, personInfoCallback)
# 循环等待回调函数
rospy.spin()
if __name__ == '__main__':
person_subscriber()
运行publisher和subscriber
1.打开终端1
roscore
2.catkin_ws下打开终端2
source devel/setup.bash
rosrun learning_topic person_publisher.py
3.catkin_ws下打开终端3
source devel/setup.bash
rosrun learning_topic person_subscriber.py