ROS 基础入门指南
ROS 基础入门指南
ROS 概述与环境搭建
ROS(Robot Operating System)是一个用于编写机器人软件的灵活框架。它提供了硬件抽象、设备驱动、库、可视化工具、消息传递和包管理等功能。
环境搭建
个人没有在虚拟机装 VSCode,使用 VSCode 的远程连接进行开发,体验也不错,就是有些模块还是得到虚拟机操作,比如小乌龟模块启动运行。引出的虚拟机如何固定 IP 之后做详细记录。
一、话题通信(Topic)
话题通信是 ROS 中最常用的通信方式,采用发布/订阅模型,实现节点间异步通信。
话题通信 C++ 实现流程
发布方逻辑:
- 包含头文件
- 初始化 ROS 节点:命名(唯一)
- 实例化 ROS 句柄
- 实例化 发布者 对象
- 组织被发布的数据,并编写逻辑发布数据
订阅方逻辑:
- 包含头文件
- 初始化 ROS 节点:命名(唯一)
- 实例化 ROS 句柄
- 实例化 订阅者 对象
- 处理订阅的消息(回调函数)
- 设置循环调用回调函数
C++ 代码示例
发布方:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int a=1,b=1;
int feibo(int x)
{
int tmp=b;
if(x==1||x==2)
return 1;
else
{
b+=a;
a=tmp;
return b;
}
}
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"fabuzhe");
ros::NodeHandle nh;
ros::Publisher pub=nh.advertise<std_msgs::String>("fibonacci",10);
std_msgs::String msg;
ros::Rate rate(10);
int count=0;
ros::Duration(1).sleep();// 留时间来在master中注册
while(ros::ok())
{
count++;
std::stringstream ss;
ss<<feibo(count);
msg.data=ss.str();
pub.publish(msg);
ROS_INFO("发布的数据是%s",ss.str().c_str());
rate.sleep();
ros::spinOnce();//官方建议的处理回调函数
}
return 0;
}
订阅方:
#include "ros/ros.h"
#include "std_msgs/String.h"
void doMSG(const std_msgs::String::ConstPtr &msg)
{
ROS_INFO("%s ",msg->data.c_str());
}
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"jieshouzhe");
ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe<std_msgs::String>("fibonacci",10,doMSG);
ros::spin();
return 0;
}
话题通信 Python 实现流程
发布者逻辑:
- 导包
- 初始化 ROS 节点:命名(唯一)
- 实例化 发布者 对象
- 组织被发布的数据,并编写逻辑发布数据
订阅者逻辑:
- 导包
- 初始化 ROS 节点:命名(唯一)
- 实例化 订阅者 对象
- 处理订阅的消息(回调函数)
- 设置循环调用回调函数
Python 代码示例
发布者:
#! /usr/bin/env python
import rospy
from std_msgs.msg import String
a=1
b=1
def js(x):
global a,b
if(x==1 or x==2):
return 1
else:
tmp=b
b+=a
a=tmp
return b
if __name__ == "__main__":
rospy.init_node("tolker")
pub=rospy.Publisher("fibonacci",String,queue_size=10)
msg=String()
rate=rospy.Rate(1)
count=0
rospy.sleep(1)
while not rospy.is_shutdown():
count+=1
ans=js(count)
msg.data=str(ans)
pub.publish(msg)
rospy.loginfo("输出的数据为:%s",msg.data)
rate.sleep()
订阅者:
#! /usr/bin/env python
import rospy
from std_msgs.msg import String
def doMSG(msg):
rospy.loginfo("%s ",msg.data)
if __name__ == "__main__":
rospy.init_node("listener")
sub=rospy.Subscriber("fibonacci",String,doMSG,queue_size=10)
rospy.spin()
二、服务通信(Service)
服务通信采用请求/响应模型,实现节点间同步通信。
服务通信 C++ 实现
服务端逻辑:
- 包含头文件
- 初始化 ROS 节点
- 创建 ROS 句柄
- 创建 服务 对象
- 回调函数处理请求并产生响应
- 由于请求有多个,需要调用 ros::spin()
客户端逻辑:
- 包含头文件
- 初始化 ROS 节点
- 创建 ROS 句柄
- 创建 客户端 对象
- 请求服务,接收响应
C++ 代码示例
服务端:
#include "ros/ros.h"
#include "plubing_server_client/addint.h"
bool doADD(plubing_server_client::addint::Request &req,plubing_server_client::addint::Response &res)
{
int num1=req.num1;
int num2=req.num2;
ROS_INFO("收到数据:%d,%d",num1,num2);
int sum=num1+num2;
res.sum=sum;
ROS_INFO("求得的数据:%d",sum);
return true;
}
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"server");
ros::NodeHandle nh;
ROS_INFO("服务已经启动------");
ros::ServiceServer server=nh.advertiseService("addint",doADD);
ros::spin();
return 0;
}
客户端:
#include "ros/ros.h"
#include "plubing_server_client/addint.h"
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
if(argc!=3)
{
ROS_INFO("提交的参数个数错误");
}
else
{
ros::init(argc,argv,"client");
ros::NodeHandle nh;
ros::ServiceClient client=nh.serviceClient<plubing_server_client::addint>("addint");
plubing_server_client::addint ai;
ai.request.num1=atoi(argv[1]);
ai.request.num2=atoi(argv[2]);
client.waitForExistence();//判断服务端是否启动,从而决定是否挂起
bool flag=client.call(ai);
if(flag)
{
ROS_INFO("响应成功");
ROS_INFO("响应结果为:%d",ai.response.sum);
}
else
{
ROS_INFO("响应失败");
}
}
return 0;
}
服务通信 Python 实现
服务端:
#! /usr/bin/env python
import rospy
from plubing_server_client.srv import addint,addintRequest,addintResponse
def doreq(req):
sum=req.num1+req.num2
response=addintResponse()
response.sum=sum
rospy.loginfo("服务器响应:%d+%d=%d",req.num1,req.num2,response.sum)
return response
if __name__ =="__main__":
rospy.init_node("pyserver")
server=rospy.Service("addint",addint,doreq)
rospy.loginfo("服务已启动--------")
rospy.spin()
客户端:
#! /usr/bin/env python
import rospy
from plubing_server_client.srv import *
import sys
if __name__ == "__main__":
if len(sys.argv)!=3:
rospy.logerr("请正确提交参数")
sys.exit(1)
rospy.init_node("pyclient")
client=rospy.ServiceProxy("addint",addint)
client.wait_for_service()
req=addintRequest()
req.num1=int(sys.argv[1])
req.num2=int(sys.argv[2])
response=client.call(req.num1,req.num2)
rospy.loginfo("收到回复:%d",response.sum)
三、参数服务器
参数服务器就是一个存放参数的容器,相当于全局变量的设置。
设置参数(C++)
#include "ros/ros.h"
int main(int argc,char *argv[])
{
ros::init(argc,argv,"setnode");
ros::NodeHandle nh;
nh.setParam("type","xiaohua");
nh.setParam("radiu",0.5);
nh.setParam("radiu",0.7);//可直接覆盖
//ros::param::set() 另一种可用方法
return 0;
}
获取和删除参数(C++)
#include "ros/ros.h"
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"param_get");
ros::NodeHandle nh;
double radiu=nh.param("radiu",0.5);
ROS_INFO("radiu=%.2f",radiu);
// getParam(键,存储结果的变量)
// 存在,返回 true,且将值赋值给参数2
// 如果键不存在,那么返回值为 false,且不为参数2赋值
// getParamCached键(比getparam更高性能,功能相同)
std::vector<std::string> names;
nh.getParamNames(names); //遍历元素
for (auto &&name : names)
{
ROS_INFO("遍历的元素为:%s",name.c_str());
}
bool flag;
flag=nh.deleteParam("audiu");
// deleteParam("键")
// 根据键删除参数,删除成功,返回 true,否则(参数不存在),返回 false
return 0;
}
设置参数(Python)
#! /usr/bin/env python
import rospy
if __name__ =="__main__":
rospy.init_node("param_set")
rospy.set_param("shuzi",10)
rospy.set_param("xiaoshu",10.5)
rospy.set_param("shuzi",10)
获取和删除参数(Python)
#! /usr/bin/env python
import rospy
if __name__ == "__main__":
rospy.init_node("param_get")
int_value=rospy.get_param("shuzi")#获取键值
double_value=rospy.get_param("xiaoshu")
rospy.loginfo("intvalue=%d",int_value)
rospy.loginfo("intvalue=%.2f",double_value)
#rospy.get_param_cached()有缓存效率更高
names=rospy.get_param_names()#获取所有元素
for i in names:
rospy.loginfo("name=%s",i)
flag=rospy.has_param("shuzi")#判断是否包含某个键
if(flag):
rospy.loginfo("存在")
shuzi=rospy.search_param("shuzi")#查找键
rospy.loginfo("%s",shuzi)
try:
rospy.delete_param("shuzi")#删除键
except Exception as e:
rospy.loginfo("删除失败")
常用 API
初始化
// C++
ros::init(argc,argv,"name");
// Python
rospy.init_node("name");
话题与服务相关对象
略(具体实现见上述代码示例)
总结
ROS 提供了三种主要的通信机制:
- 话题通信(Topic) - 异步通信,适用于连续数据流
- 服务通信(Service) - 同步通信,适用于请求/响应模式
- 参数服务器(Parameter Server) - 全局参数存储
掌握这三种通信机制是 ROS 开发的基础,后续的机器人开发都是基于这些通信机制实现的。
Enjoy Reading This Article?
Here are some more articles you might like to read next: