Ubuntu20.04安装ROS Noetic

文章目录

    • 一、ROS基础概述
      • 1、操作系统安装
      • 2、ROS Noetic安装
    • 二、核心概念
      • 1、ROS介
      • 2、ROS核心概念
      • 3、ROS常用命令行工具
    • 三、编程基础
      • 1、工作空间创建
      • 2、Topic话题通信
        • 1、发布者publisher编程实现
        • 2、订阅者subscriber编程实现
        • 3、自定义话题通信
          • 1、C++文件
          • 2、python文件
      • 3、Service服务通信
        • 1、客户端Client编程实现
        • 2、服务端Service编程实现
        • 3、自定义数据服务
          • 1、C++文件
          • 2、python文件
      • 4、全局参数使用与编程
          • 1、C++文件
          • 2、python文件
    • 四、ROS常用组件
      • 1、ROS坐标变换
        • 1、简单介绍与使用
        • 2、广播与监听编程实现
      • 2、launch启动文件
        • 1、基本介绍
        • 2、launch简单示例
      • 3、可视化工具
    • 五、学习资料

一、ROS基础概述

1、操作系统安装

ROS目前只能在基于Unix的平台上运行,因此我们使用Ubuntu来作为ROS的系统。这里我们安装了Ubuntu20.04ROS Noetic Ninjemys。本机和虚拟机安装详见以下文章,选其中一种即可

笔记本安装 Windows10 和 Ubuntu20.04 双系统

VMWare虚拟机安装Ubuntu20.04详细过程

2、ROS Noetic安装

官网ROS Noetic安装教程参考:https://wiki.ros.org/cn/noetic/Installation/Ubuntu

1、配置系统软件源

打开“软件更新”,进入到“Ubuntu软件“页面,允许universe、restricted、multiverse三项,源换成国内源即可。

2、添加ROS软件源

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'

3、设置密钥

sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

4、安装ROS

#更新apt索引包
sudo apt update
#安装桌面完整版,若网络原因失败可多安装几次或使用手机热点
sudo apt install ros-noetic-desktop-full

5、设置环境

echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc

6、小海龟测试

终端输入roscore可以查看安装好的ros的发行版名称为noetic和版本号;ctrl+Alt+t再次打开一个新的终端,输入rosrun turtlesim turtlesim_node,即会出现小海龟的仿真界面;再次打开新终端,输入rosrun turtlesim turtle_teleop_key,即可控制小海龟移动了。
Ubuntu20.04安装ROS Noetic_第1张图片

二、核心概念

1、ROS介

ROS (Robot Operating System, 机器人操作系统) 提供一系列程序库和工具以帮助软件开发者创建机器人应用软件。它提供了硬件抽象、设备驱动、函数库、可视化工具、消息传递和软件包管理等诸多功能。

ROS中文官网:https://wiki.ros.org/cn

2、ROS核心概念

节点与节点管理器

  • 节点(Node)—— 执行单元
    执行具体任务的进程、独立运行的可执行文件;不同节点可使用不同的编程语言,可分布式运行在不同的主机;节点在系统中的名称必须是唯一的。

  • 节点管理器 (ROS Master)—— 控制中心
    为节点提供命名和注册服务;跟踪和记录话题/服务通信,辅助节点相互查找、 建立连接;提供参数服务器,节点使用此服务器存储和检索 运行时的参数。

话题通信

  • 话题(Topic)—— 异步通信机制
    节点间用来传输数据的重要总线;使用发布(Publisher)/订阅(Subscriber)模型,数据由发布者传输到订阅者,同一个话题的订阅者或发布者可以不唯一。

  • 消息(Message)——话题数据

    话题的具体数据称为消息(Message),使用.msg文件定义;消息用来描述话题当中具体的数据类型,在ROS中,有些消息已经被预定义了,比如雷达、图像等,也可以自定义消息。

服务通信

  • 服务(Service)—— 同步通信机制
    使用客户端(Service)/服务器(Client)模型,客户端发送请求数据,服务器完成 处理后返回应答数据。使用.src文件定义。

话题与服务区别

话题 服务
同步性 异步 同步
通信模型 发布/订阅 服务器/客户端
底层协议 ROSTCP/ROSUDP ROSTCP/ROSUDP
反馈机制
缓冲区
实时性
节点关系 多对多 一对多(一个server)
适用场景 数据传输 逻辑处理

参数

  • 参数(Parameter)—— 全局共享字典
    可通过网络访问的共享、多变量字典;节点使用此服务器来存储和检索运行时的参数;适合存储静态、非二进制的配置参数,不适合存储动态配置的数据。

文件系统

  • 功能包(Package): ROS软件中的基本单元,包含节点源码、配置文件、数据定义等。

  • 功能包清单(Package manifest):记录功能包的基本信息,包含作者信息、许可信息、依赖选项、编译标志等。

  • 元功能包(Meta Packages):组织多个用于同一目的功能包。

3、ROS常用命令行工具

#分别在四个终端打开运行
#启动ROS Master
roscore
#启动海龟仿真节点
rosrun turtlesim turtlesim_node
#启动海龟控制节点
rosrun turtlesim turtle_teleop_key
#查看系统运行计算图
rqt_graph 

rosnode节点命令

#查看活动节点
rosnode list
#查看某一节点具体信息
rosnode info /turtlesim

rostopic话题命令

#以10Hz的频率发布话题移动海龟,这里命令用双击tab补全后修改
#速度为m/s,角度为弧度/s
rostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist "linear:
  x: 1.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 1.0" 

rosmsg消息命令

#查看消息
rosmsg show geometry_msgs/Twist

rosservice服务命令

#打印可用服务
rosservice list
#创建一个新海龟
rosservice call /spawn "x: 2.0
y: 2.0
theta: 0.0
name: 'turtle2'" 

rosbag命令

#保存所有步骤,-O代表保存的压缩包路径
rosbag record -a -O cmd_record
#复原所有步骤
rosbag play cmd_record.bag

另外还有rosparam、rossrv等命令

三、编程基础

1、工作空间创建

工作空间(workspace)是一个存放工程开发相关文件的文件夹。

  • src:代码空间(Source Space)
  • build:编译空间(Build Space)
  • devel:开发空间(Development Space)
  • install:安装空间(Install Space)

1、创建工作空间catkin_ws

#创建目录
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
#初始化文件夹
catkin_init_workspace
#src文件中创建了一个 CMakeLists.txt 的文件,目的是告诉系统,这个是ROS的工作空间。

**2、编译工作空间 **

#需要在整个工作目录下编译
#编译完成后,会出现build 和 devel两个文件夹
cd ~/catkin_ws/
catkin_make
#生成install文件夹
catkin_make install

3、设置环境变量

echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
#查看当前环境变量
echo $ROS_PACKAGE_PATH

4、创建功能包

#std_msgs:包含常见消息类型
#roscpp:使用C++实现ROS各种功能
#rospy:使用python实现ROS各种功能
#test_pkg就是其中一个功能包,还可以下载其他功能包
cd ~/catkin_ws/src
catkin_create_pkg test_pkg std_msgs roscpp rospy

package.xml是功能包的描述文件,CMakeLists.txt定义编译规则,编译出的文件在devel目录下的include文件夹(头文件)和lib文件夹(可执行文件)

5、编译功能包

cd ~/catkin_ws
catkin_make

2、Topic话题通信

Ubuntu20.04安装ROS Noetic_第2张图片
![在这里插入图片描述](https://img-blog.csdnimg.cn/0a2eccec8b7e4e7b851342ce376981fc.png

下载功能包

#下面的服务功能都需要依赖以下功能包
cd ~/catkin_ws/srcs
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim

1、发布者publisher编程实现

指发布程序代码给海龟仿真器,控制海龟运动

1、编写发布者代码

发布者流程

  • 初始化ROS节点;
  • 向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型;
  • 创建消息数据;
  • 按照一定频率循环发布消息。

以下两种语言二选一即可

/**
 * 该例程将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist
 * 将代码保存在功能包下的src目录下,并命名为velocity_publisher.cpp
 */
 
#include 
#include 

int main(int argc, char **argv)
{
	// ROS节点初始化
	ros::init(argc, argv, "velocity_publisher");
	// 创建节点句柄
	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);
	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;
}

python文件需要确保文件拥有可执行权限,这里我把python文件都放在了scripts文件夹下,在此打开终端,运行chmod +x *.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#python程序,区分与cpp文件,将代码保存在功能包下的scripts目录下,并命名为velocity_publisher.py
# 该例程将发布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

2、定义编译规则

仅C++代码需要定义编译规则,python代码可直接运行

#打开功能包的CMakeLists.txt文件,在build下添加以下规则
#编译
add_executable(velocity_publisher src/velocity_publisher.cpp)
#链接
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})

3、编译

仅C++代码需要编译,python代码可直接运行

#要在工作空间下编译
cd ~/catkin_ws
catkin_make

4、运行

运行成功后海龟按照发布者的程序进行运动

#首先刷新一下环境变量,放入本地环境变量了也可以不刷新
source ~/catkin_ws/devel/setup.bash
#分别在三个终端打开,就可以看见海龟运行了
roscore
rosrun turtlesim turtlesim_node
#python运行以下命令
#若出现“python3”: 没有那个文件或目录,则需要更改py文件中的python解释器
rosrun learning_topic velocity_publisher.py
#C++运行下面命令
rosrun learning_topic velocity_publisher

2、订阅者subscriber编程实现

流程步骤与发布者相似,订阅者可以监听海龟的位置信息变化并打印在屏幕

订阅者流程

  • 初始化ROS节点;
  • 订阅需要的话题;
  • 循环等待话题消息,接收到消息后进入回调函数;
  • 在回调函数中完成消息处理。

C++订阅代码

/**
 * 该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
 */
#include 
#include "turtlesim/Pose.h"
// 接收到订阅的消息后,会进入消息回调函数
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
    ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);
    // 循环等待回调函数
    ros::spin();
    return 0;
}

python订阅代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将订阅/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、自定义话题通信

ROS Master作为节点管理者,负责节点之间的通信连接,一旦发布者和消息者之间连通后,master关机后两者仍可通信,但无法更改对象
Ubuntu20.04安装ROS Noetic_第3张图片

learning_topic文件夹下创建msg文件夹,在里面创建Person.msg文件(注意一定要叫msg文件夹,在CMakeLists.txt规则有写),定义话题消息

string name
uint8 sex
uint8 age

uint8 unknown=0
uint8 male=1
uint8 female=2

在功能包的package.xml文件下添加依赖

<build_depend>message_generationbuild_depend> 
<exec_depend>message_runtimeexec_depend>

CMakeLists.txt文件下添加编译规则,C++和python都需要。找到对应的配置文件下添加编译选项

find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  rospy
  std_msgs
  turtlesim
  #添加
  message_generation
)
#添加
add_message_files(
   FILES
   Person.msg
)
#添加
generate_messages(DEPENDENCIES
  std_msgs
)
catkin_package(
  #添加
  CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
)

最后编译生成相关依赖

cd ~/catkin_ws
#编译
catkin_make 
1、C++文件

C++文件放置在功能包learning_topic下的src文件夹

发布者

/**
 * 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
 */
#include 
#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;
}

订阅者

/**
 * 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
 */
#include 
#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;
}

CMakeLists.txt文件下的build下添加编译规则

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)

编译与运行

cd ~/catkin_ws
catkin_make
roscore
rosrun learning_topic person_publisher
rosrun learning_topic person_subscriber
2、python文件

py文件都放置在功能包learning_topic下的scripts文件夹

发布者

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将发布/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

订阅者

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将订阅/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()

运行

#获得可执行权限
chmod +x ~/catkin_ws/src/learning_topic/scripts/*.py
cd ~/catkin_ws
rosrun learning_topic person_subscriber.py
rosrun learning_topic person_publisher.py 

3、Service服务通信

Ubuntu20.04安装ROS Noetic_第4张图片

下载功能包

cd ~/catkin_ws/src
catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim

1、客户端Client编程实现

C++代码

/**
 * 该例程将请求/spawn服务,服务数据类型turtlesim::Spawn
 */
#include 
#include 
int main(int argc, char** argv)
{
    // 初始化ROS节点
	ros::init(argc, argv, "turtle_spawn");
    // 创建节点句柄
	ros::NodeHandle node;
    // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
	ros::service::waitForService("/spawn");
	ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    // 初始化turtlesim::Spawn的请求数据
	turtlesim::Spawn srv;
	srv.request.x = 2.0;
	srv.request.y = 2.0;
	srv.request.name = "turtle2";
    // 请求服务调用
	ROS_INFO("Call service to spwan turtle[x:%0.6f, y:%0.6f, name:%s]", 
			 srv.request.x, srv.request.y, srv.request.name.c_str());
	add_turtle.call(srv);
	// 显示服务调用结果
	ROS_INFO("Spwan turtle successfully [name:%s]", srv.response.name.c_str());
	return 0;
};

CMakeLists.txt增加编译规则

add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${catkin_LIBRARIES})

运行

cd ~/catkin_ws
#编译
catkin_make
#三个窗口打开
roscore
rosrun turtlesim turtlesim_node
osrun learning_service turtle_spawn

python代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将请求/spawn服务,服务数据类型turtlesim::Spawn
import sys
import rospy
from turtlesim.srv import Spawn

def turtle_spawn():
	# ROS节点初始化
    rospy.init_node('turtle_spawn')
	# 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    rospy.wait_for_service('/spawn')
    try:
        add_turtle = rospy.ServiceProxy('/spawn', Spawn)
		# 请求服务调用,输入请求数据
        response = add_turtle(2.0, 2.0, 0.0, "turtle2")
        return response.name
    except rospy.ServiceException as e:
        print("Service call failed: %s"%e)
if __name__ == "__main__":
	#服务调用并显示调用结果
    print("Spwan turtle successfully [name:%s]" %(turtle_spawn()))

运行

cd ~/catkin_ws
#三个窗口打开
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_spawn.py

2、服务端Service编程实现

C++代码

/**
 * 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger
 */
#include 
#include 
#include 
ros::Publisher turtle_vel_pub;
bool pubCommand = false;
// service回调函数,输入参数req,输出参数res
bool commandCallback(std_srvs::Trigger::Request  &req,
         			std_srvs::Trigger::Response &res)
{
	pubCommand = !pubCommand;
    // 显示请求数据
    ROS_INFO("Publish turtle velocity command [%s]", pubCommand==true?"Yes":"No");
	// 设置反馈数据
	res.success = true;
	res.message = "Change turtle command state!"
    return true;
}
int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "turtle_command_server");
    // 创建节点句柄
    ros::NodeHandle n;
    // 创建一个名为/turtle_command的server,注册回调函数commandCallback
    ros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback);
	// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
	turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
    // 循环等待回调函数
    ROS_INFO("Ready to receive turtle command.");
	// 设置循环的频率
	ros::Rate loop_rate(10);
	while(ros::ok())
	{
		// 查看一次回调函数队列
    	ros::spinOnce();
		// 如果标志为true,则发布速度指令
		if(pubCommand)
		{
			geometry_msgs::Twist vel_msg;
			vel_msg.linear.x = 0.5;
			vel_msg.angular.z = 0.2;
			turtle_vel_pub.publish(vel_msg);
		}
		//按照循环频率延时
	    loop_rate.sleep();
	}
    return 0;
}

python代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger
import rospy
import _thread,time
from geometry_msgs.msg import Twist
from std_srvs.srv import Trigger, TriggerResponse
pubCommand = False;
turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
def command_thread():	
	while True:
		if pubCommand:
			vel_msg = Twist()
			vel_msg.linear.x = 0.5
			vel_msg.angular.z = 0.2
			turtle_vel_pub.publish(vel_msg)
			
		time.sleep(0.1)
def commandCallback(req):
	global pubCommand
	pubCommand = bool(1-pubCommand)
	# 显示请求数据
	rospy.loginfo("Publish turtle velocity command![%d]", pubCommand)
	# 反馈数据
	return TriggerResponse(1, "Change turtle command state!")
def turtle_command_server():
	# ROS节点初始化
    rospy.init_node('turtle_command_server')
	# 创建一个名为/turtle_command的server,注册回调函数commandCallback
    s = rospy.Service('/turtle_command', Trigger, commandCallback)
	# 循环等待回调函数
    print("Ready to receive turtle command.")
    _thread.start_new_thread(command_thread, ())
    rospy.spin()
if __name__ == "__main__":
    turtle_command_server()

其他操作和客户端相似,海龟启动后通过rosservice call /turtle_command命令改变海龟运动状态

3、自定义数据服务

自定义请求和应答内容,通过请求进行控制

Ubuntu20.04安装ROS Noetic_第5张图片

learning_service文件夹下创建srv文件夹,在里面创建Person.srv文件(注意一定要叫srv文件夹,在CMakeLists.txt规则有写),定义数据。三杠上方代表Request参数,下方代表Response参数

string name
uint8 age
uint8 sex
uint8 unknown=0
uint8 male=1
uint8 female=2
---
string result

在功能包的package.xml文件下添加依赖

<build_depend>message_generationbuild_depend> 
<exec_depend>message_runtimeexec_depend>

CMakeLists.txt文件下添加编译规则,C++和python都需要。找到对应的配置文件下添加编译选项

find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  rospy
  std_msgs
  turtlesim
  #找到功能包作为依赖
  message_generation
)
#需要让编译器找到文件在哪
add_service_files(
   FILES
   Person.srv
)
#产生文件依赖
generate_messages(DEPENDENCIES
  std_msgs
)
catkin_package(
  #添加
  CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
)

最后编译生成相关依赖

cd ~/catkin_ws
#编译
catkin_make 
1、C++文件

服务器

/**
 * 该例程将执行/show_person服务,服务数据类型learning_service::Person
 */

#include 
#include "learning_service/Person.h"
// service回调函数,输入参数req,输出参数res
bool personCallback(learning_service::Person::Request  &req,
         			learning_service::Person::Response &res)
{
    // 显示请求数据
    ROS_INFO("Person: name:%s  age:%d  sex:%d", req.name.c_str(), req.age, req.sex);
	// 设置反馈数据
	res.result = "OK";
    return true;
}
int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "person_server");
    // 创建节点句柄
    ros::NodeHandle n;
    // 创建一个名为/show_person的server,注册回调函数personCallback
    ros::ServiceServer person_service = n.advertiseService("/show_person", personCallback);
    // 循环等待回调函数
    ROS_INFO("Ready to show person informtion.");
    ros::spin();
    return 0;
}

客户端

/**
 * 该例程将请求/show_person服务,服务数据类型learning_service::Person
 */
#include 
#include "learning_service/Person.h"

int main(int argc, char** argv)
{
    // 初始化ROS节点
	ros::init(argc, argv, "person_client");
    // 创建节点句柄
	ros::NodeHandle node;
    // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
	ros::service::waitForService("/show_person");
	ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");
    // 初始化learning_service::Person的请求数据
	learning_service::Person srv;
	srv.request.name = "Tom";
	srv.request.age  = 20;
	srv.request.sex  = learning_service::Person::Request::male;
    // 请求服务调用
	ROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", 
			 srv.request.name.c_str(), srv.request.age, srv.request.sex);
	person_client.call(srv);
	// 显示服务调用结果
	ROS_INFO("Show person result : %s", srv.response.result.c_str());
	return 0;
};

CMakeLists.txt文件下的build下添加编译规则

add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${catkin_LIBRARIES})
add_dependencies(person_server ${PROJECT_NAME}_gencpp)

add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
add_dependencies(person_client ${PROJECT_NAME}_gencpp)

编译运行

cd ~/catkin_ws
catkin_make
roscore
rosrun learning_service person_server
rosrun learning_service person_client
2、python文件

服务器

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将执行/show_person服务,服务数据类型learning_service::Person
import rospy
from learning_service.srv import Person, PersonResponse
def personCallback(req):
	# 显示请求数据
    rospy.loginfo("Person: name:%s  age:%d  sex:%d", req.name, req.age, req.sex)
	# 反馈数据
    return PersonResponse("OK")
def person_server():
	# ROS节点初始化
    rospy.init_node('person_server')
	# 创建一个名为/show_person的server,注册回调函数personCallback
    s = rospy.Service('/show_person', Person, personCallback)
	# 循环等待回调函数
    print("Ready to show person informtion.")
    rospy.spin()
if __name__ == "__main__":
    person_server()

客户端

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将请求/show_person服务,服务数据类型learning_service::Person
import sys
import rospy
from learning_service.srv import Person, PersonRequest
def person_client():
	# ROS节点初始化
    rospy.init_node('person_client')
	# 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    rospy.wait_for_service('/show_person')
    try:
        person_client = rospy.ServiceProxy('/show_person', Person)

		# 请求服务调用,输入请求数据
        response = person_client("Tom", 20, PersonRequest.male)
        return response.result
    except rospy.ServiceException as e:
        print("Service call failed: %s"%e)
if __name__ == "__main__":
	#服务调用并显示调用结果
    print "Show person result : %s" %(person_client())

运行

#获得可执行权限
chmod +x ~/catkin_ws/src/learning_service/scripts/*.py
#三个终端打开
roscore
rosrun learning_service person_server
rosrun learning_service person_client

4、全局参数使用与编程

下载功能包

cd ~/catkin_ws/src
catkin_create_pkg learning_parameter roscpp rospy std_srvs

rosparam基本命令

#列出当前多有参数
rosparam list
#显示某个参数值
rosparam get param_key
#设置某个参数值
rosparam set param_key param_value
#保存参数到文件
rosparam dump file_name
#从文件读取参数
rosparam load file_name
#删除参数
rosparam delete param_key
1、C++文件

和前面一样,需要添加编译规则

/**
 * 该例程设置/读取海龟例程中的参数
 */
#include 
#include 
#include 

int main(int argc, char **argv)
{
	int red, green, blue;
    // ROS节点初始化
    ros::init(argc, argv, "parameter_config");
    // 创建节点句柄
    ros::NodeHandle node;
    // 读取背景颜色参数
	ros::param::get("/turtlesim/background_r", red);
	ros::param::get("/turtlesim/background_g", green);
	ros::param::get("/turtlesim/background_b", blue);
	ROS_INFO("Get Backgroud Color[%d, %d, %d]", red, green, blue);
	// 设置背景颜色参数
	ros::param::set("/turtlesim/background_r", 255);
	ros::param::set("/turtlesim/background_g", 255);
	ros::param::set("/turtlesim/background_b", 255);
	ROS_INFO("Set Backgroud Color[255, 255, 255]");
    // 读取背景颜色参数
	ros::param::get("/turtlesim/background_r", red);
	ros::param::get("/turtlesim/background_g", green);
	ros::param::get("/turtlesim/background_b", blue);
	ROS_INFO("Re-get Backgroud Color[%d, %d, %d]", red, green, blue);
	// 调用服务,刷新背景颜色
	ros::service::waitForService("/clear");
	ros::ServiceClient clear_background = node.serviceClient<std_srvs::Empty>("/clear");
	std_srvs::Empty srv;
	clear_background.call(srv);
	sleep(1);
    return 0;
}

添加编译规则

add_executable(parameter_config src/parameter_config.cpp)
target_link_libraries(parameter_config ${catkin_LIBRARIES})

运行

cd ~/catkin_ws
catkin_make 
roscore rosrun turtlesim turtlesim_node 
rosrun learning_parameter parameter_config
2、python文件
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程设置/读取海龟例程中的参数
import sys
import rospy
from std_srvs.srv import Empty
def parameter_config():
	# ROS节点初始化
    rospy.init_node('parameter_config', anonymous=True)
	# 读取背景颜色参数
    red   = rospy.get_param('/turtlesim/background_r')
    green = rospy.get_param('/turtlesim/background_g')
    blue  = rospy.get_param('/turtlesim/background_b')
    rospy.loginfo("Get Backgroud Color[%d, %d, %d]", red, green, blue)
	# 设置背景颜色参数
    rospy.set_param("/turtlesim/background_r", 255);
    rospy.set_param("/turtlesim/background_g", 255);
    rospy.set_param("/turtlesim/background_b", 255);
    rospy.loginfo("Set Backgroud Color[255, 255, 255]");
	# 读取背景颜色参数
    red   = rospy.get_param('/turtlesim/background_r')
    green = rospy.get_param('/turtlesim/background_g')
    blue  = rospy.get_param('/turtlesim/background_b')
    rospy.loginfo("Get Backgroud Color[%d, %d, %d]", red, green, blue)
	# 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    rospy.wait_for_service('/clear')
    try:
        clear_background = rospy.ServiceProxy('/clear', Empty)
		# 请求服务调用,输入请求数据
        response = clear_background()
        return response
    except rospy.ServiceException as e:
        print("Service call failed: %s"%e)
if __name__ == "__main__":
    parameter_config()

运行

cd ~/catkin_ws
roscore
rosrun turtlesim turtlesim_node 
rosrun learning_parameter parameter_config.py

四、ROS常用组件

1、ROS坐标变换

1、简单介绍与使用

#这里因为ubuntu20没有python命令,所以我复制了/usr/bin中的python3变成python
roslaunch turtle_tf turtle_tf_demo.launch
#这里可能会遇到bug
#TypeError: cannot use a string pattern on a bytes-like object
#sudo gedit /opt/ros/noetic/lib/tf/view_frames 修改89行
#m = r.search(str(vstr))
#文件会保存在当前目录下
rosrun turtlesim turtle_teleop_key
#坐标系命令行工具
rosrun tf tf_echo turtle1 turtle2
#可视化
rosrun rviz rviz -d `rospack find turtle_tf` /rviz/turtle_rviz.rviz

Ubuntu20.04安装ROS Noetic_第6张图片

2、广播与监听编程实现

如何实现一个tf广播器

  • 定义TF广播器(TransformBroadcaster)
  • 创建坐标变换值;
  • 发布坐标变换(sendTransform)

如何实现一个TF监听器

  • 定义TF监听器;(TransformListener)
  • 查找坐标变换;(waitFor Transform、lookupTransform)
cd ~/catkin_ws/src
catkin_create_pkg learning_tf roscpp rospy tf turtlesim

这里有点问题

2、launch启动文件

传统启动节点的方法:每启动一个节点都要打开一个新的终端运行一个新的命令,当系统中的节点数量许多时,显然是很不方便的,而且命令的输入也可能会发生错误。
launch启动文件的引入:可以同时启动多个节点,并且可以自动启动ROS Master节点管理器以及实现每个节点的各种配置,为多个节点的操作提供很大的便利。

1、基本介绍

< launch >
launch文件中的根元素采用< launch >标签定义,里面包含所有节点的启动配置内容
< node >

启动节点

<node pkg="turtlesim" name="sim1" type="turtlesim_node"/>
  • pkg:节点所在的功能包名称

  • type:节点的可执行文件名称

  • name:节点运行时的名称

  • output, respawn, required, ns, args

< param > / < rosparam > 参数
设置ROS系统运行中的参数,存储在参数服务器中。

<param name="output_frame" value="odom"/>
  • name:参数名
  • value:参数值

加载参数文件中的多个参数:

<rosparam file="params.yaml" command="load" ns="params" />

< arg > 参数
launch文件内部的局部变量,仅限于launch文件使用

<arg name="arg-name" default="arg-value" />
  • name:参数名
  • value:参数值

param设置的参数是存储在ROS参数服务器中的,而arg设置的参数仅限于launch文件中
< remap > 重映射
重映射ROS计算图资源的命名。

<remap from="/turtlebot/cmd_vel" to="/cmd_vel"/>
  • from:原命名
  • to:映射之后的命名

< include > 嵌套
包含其他launch文件,类似C语言中的头文件包含。

<include file="$(dirname)/other.launch" />
  • file:包含的其他launch文件路径

2、launch简单示例

首先创建功能包,在里面创建launch文件夹,一般launch都放在里面(可自定义文件夹名)

cd ~/catkin_ws/src
catkin_create_pkg learning_launch

简单订阅者发布者

名为simple.launch

<launch>
    <node pkg="learning_topic" type="person_subscriber" name="talker" output="screen" />
    <node pkg="learning_topic" type="person_publisher" name="listener" output="screen" /> 
launch>
#需要编译才能识别
cd ~/catkin_ws
catkin_make
#若找不到功能包
#运行rospack profile

#一键启动
roslaunch learning_launch simple.launch

config的launch文件

config/param.yml文件下

A: 123
B: "hello"
group:
  C: 456
  D: "hello"

turtlesim_parameter_config.launch文件

<launch>
	<param name="/turtle_number"   value="2"/>
    <node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">
		<param name="turtle_name1"   value="Tom"/>
		<param name="turtle_name2"   value="Jerry"/>
		<rosparam file="$(find learning_launch)/config/param.yaml" command="load"/>
	node>
    <node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop_key" output="screen"/>
launch>

运行,可以看见节点参数的不同,不同位置显示的参数位置也不同

#回到工作空间目录运行
roslaunch learning_launch turtlesim_parameter_config.launch 

![在这里插入图片描述](https://img-blog.csdnimg.cn/206bb6edfadd434eb78e8421fe815b4b.png

remap使用

<launch>
	<include file="$(find learning_launch)/launch/simple.launch" />
    <node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">
		<remap from="/turtle1/cmd_vel" to="/cmd_vel"/>
	node>
launch>

3、可视化工具

rqt_console:日志输出工具

rqt_graph:计算图可视化工具

rqt_plot:数据绘图工具

rqt_image_view:图像渲染工具

Rviz:三维可视化工具

GazeBo:三维物理仿真平台

五、学习资料

ROS: https://www.ros.org
ROS Wiki: http://wiki.ros.org
ROSCon 2012~2019:https://roscon.ros.org
ROS Robots: https://robots.ros.org
Ubuntu Wiki: https://wiki.ubuntu.org.cn
古月居:http://www.guyuehome.com
古月居泡泡:https://www.guyuehome.com/Bubble
古月学院:https://class.guyuehome.com
如何学习ROS:
https://mp.weixin.qq.com/s/uYvGuiG-TlOalWUynR2Nzg
一起从零手写URDF模型:
https://class.guyuehome.com/detail/p5eleea4fe1e5c_lgm126Xn/6
如何从Solidworks导出URDF模型
https://class.guyuehome.com/detail/p5e32dce7906e0_6TqS7BwX/6
如何在Gazebo中实现移动机器人仿真:
https://class.guyuehome.com/detail/p_5eb2366befe4aE4rbNmXt/6
Movelt可视化配置及仿真指南
https://class.guyuehome.com/detail/p_5e71966b3fdfd_g4DpRGg9/6

你可能感兴趣的:(#,ROS,linux)