ROS目前只能在基于Unix的平台上运行,因此我们使用Ubuntu来作为ROS的系统。这里我们安装了Ubuntu20.04
和ROS Noetic Ninjemys
。本机和虚拟机安装详见以下文章,选其中一种即可
笔记本安装 Windows10 和 Ubuntu20.04 双系统
VMWare虚拟机安装Ubuntu20.04详细过程
官网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
,即可控制小海龟移动了。
ROS (Robot Operating System, 机器人操作系统) 提供一系列程序库和工具以帮助软件开发者创建机器人应用软件。它提供了硬件抽象、设备驱动、函数库、可视化工具、消息传递和软件包管理等诸多功能。
ROS中文官网:https://wiki.ros.org/cn
节点与节点管理器
节点(Node)—— 执行单元
执行具体任务的进程、独立运行的可执行文件;不同节点可使用不同的编程语言,可分布式运行在不同的主机;节点在系统中的名称必须是唯一的。
节点管理器 (ROS Master)—— 控制中心
为节点提供命名和注册服务;跟踪和记录话题/服务通信,辅助节点相互查找、 建立连接;提供参数服务器,节点使用此服务器存储和检索 运行时的参数。
话题通信
话题(Topic)—— 异步通信机制
节点间用来传输数据的重要总线;使用发布(Publisher)/订阅(Subscriber)模型,数据由发布者传输到订阅者,同一个话题的订阅者或发布者可以不唯一。
消息(Message)——话题数据
话题的具体数据称为消息(Message),使用.msg文件定义;消息用来描述话题当中具体的数据类型,在ROS中,有些消息已经被预定义了,比如雷达、图像等,也可以自定义消息。
服务通信
话题与服务区别
话题 | 服务 | |
---|---|---|
同步性 | 异步 | 同步 |
通信模型 | 发布/订阅 | 服务器/客户端 |
底层协议 | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
反馈机制 | 无 | 有 |
缓冲区 | 有 | 无 |
实时性 | 弱 | 强 |
节点关系 | 多对多 | 一对多(一个server) |
适用场景 | 数据传输 | 逻辑处理 |
参数
文件系统
功能包(Package): ROS软件中的基本单元,包含节点源码、配置文件、数据定义等。
功能包清单(Package manifest):记录功能包的基本信息,包含作者信息、许可信息、依赖选项、编译标志等。
元功能包(Meta Packages):组织多个用于同一目的功能包。
#分别在四个终端打开运行
#启动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等命令
工作空间(workspace)是一个存放工程开发相关文件的文件夹。
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
![在这里插入图片描述](https://img-blog.csdnimg.cn/0a2eccec8b7e4e7b851342ce376981fc.png
下载功能包
#下面的服务功能都需要依赖以下功能包
cd ~/catkin_ws/srcs
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim
指发布程序代码给海龟仿真器,控制海龟运动
1、编写发布者代码
发布者流程
以下两种语言二选一即可
/**
* 该例程将发布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
流程步骤与发布者相似,订阅者可以监听海龟的位置信息变化并打印在屏幕
订阅者流程
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()
ROS Master作为节点管理者,负责节点之间的通信连接,一旦发布者和消息者之间连通后,master关机后两者仍可通信,但无法更改对象
在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
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
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
下载功能包
cd ~/catkin_ws/src
catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim
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
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
命令改变海龟运动状态
自定义请求和应答内容,通过请求进行控制
在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
服务器
/**
* 该例程将执行/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
服务器
#!/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
下载功能包
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
和前面一样,需要添加编译规则
/**
* 该例程设置/读取海龟例程中的参数
*/
#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
#!/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
#这里因为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
如何实现一个tf广播器
如何实现一个TF监听器
cd ~/catkin_ws/src
catkin_create_pkg learning_tf roscpp rospy tf turtlesim
这里有点问题
传统启动节点的方法:每启动一个节点都要打开一个新的终端运行一个新的命令,当系统中的节点数量许多时,显然是很不方便的,而且命令的输入也可能会发生错误。
launch启动文件的引入:可以同时启动多个节点,并且可以自动启动ROS Master节点管理器以及实现每个节点的各种配置,为多个节点的操作提供很大的便利。
< 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"/>
加载参数文件中的多个参数:
<rosparam file="params.yaml" command="load" ns="params" />
< arg > 参数
launch文件内部的局部变量,仅限于launch文件使用
<arg name="arg-name" default="arg-value" />
param设置的参数是存储在ROS参数服务器中的,而arg设置的参数仅限于launch文件中
< remap > 重映射
重映射ROS计算图资源的命名。
<remap from="/turtlebot/cmd_vel" to="/cmd_vel"/>
< include > 嵌套
包含其他launch文件,类似C语言中的头文件包含。
<include file="$(dirname)/other.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>
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