目录
一、常用API
1.初始化
def init_node(name, argv=None, anonymous=False, log_level=None, disable_rostime=False, disable_rosout=False, disable_signals=False, xmlrpc_port=0, tcpros_port=0):
"""
在ROS msater中注册节点
@param name: 节点名称,必须保证节点名称唯一,节点名称中不能使用命名空间(不能包含 '/')
@type(数据类型) name: str
@argv=None 封装节点调用时传递的参数
@param anonymous: 取值为 true 时,为节点名称后缀随机编号,解决节点名称重名问题
@type anonymous: bool
使用:
1、argv使用:可以按照ros中指定的语法格式传参,ros可以解析并加以使用
2、anonymous使用:节点名称为随机数
"""
终端中使用
2.话题与服务相关对象
2.1发布对象
对象获取:
class Publisher(Topic):
"""
在ROS master注册为相关话题的发布方
"""
def __init__(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None, queue_size=None):
"""
Constructor
@param name: 话题名称
@type name: str
@param data_class: 消息类型
@param latch: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
@type latch: bool
@应用:
@param queue_size: 等待发送给订阅者的最大消息数量
@type queue_size: int
"""
消息发布函数:
def publish(self, *args, **kwds):
"""
发布消息
"""
代码:
#! /usr/bin/env python
# coding=UTF-8
"""
需求: 实现基本的话题通信,一方发布数据,一方接收数据,
实现的关键点:
1.发送方
2.接收方
3.数据(此处为普通文本)
PS: 二者需要设置相同的话题
消息发布方:
循环发布信息:HelloWorld 后缀数字编号
实现流程:
1.导包
2.初始化 ROS 节点:命名(唯一)
3.创建发布者对象
4.编写发布逻辑并发布数据
"""
# 导包
import rospy
from std_msgs.msg import String #消息类型 std_msgs.msg导入是为了使我们可以重用std_msgs/String消息类型进行发布
if __name__ == "__main__":
#2.初始化 ROS 节点:命名(唯一
"""
作用:ROS初始化
参数:
name --- 设置节点名称
argv = None --- 封装节点调用时传递的函数
anonymous = False --- 可以为节点名称生成随机后缀,可以解决重名问题
使用:
1、argv使用:可以按照ros中指定的语法格式传参,ros可以解析并加以使用
2、anonymous使用:节点名称为随机数
"""
rospy.init_node("chenmo",anonymous=True) #传入节点名称
# 3.创建发布者对象
"""
内容: latch
bool值,默认值为False
作用: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时,会将该消息发送给新订阅者
使用: latch=Ture
"""
pub=rospy.Publisher("che",String,queue_size=10,latch=True) #话题名称,消息类型
# 4..编写发布逻辑并发布数据
# 创建数据
msg=String() #创建空的消息类型为String的消息,
msg_front="hellocehnmo"
count=0 #计数器
# 设置循环频率
rate=rospy.Rate(10)
# 循环体
while not rospy.is_shutdown():
count += 1
if count<10:
msg.data=msg_front+str(count)
# 发布数据 #发布对象: 前面创建的pub, 发布函数:.publish(),发布数据:前面创建的消息msg, 消息类型String
pub.publish(msg) #将字符串发布到chatter话题上
# 此循环调用rospy.loginf(), 它执行三项任务:将消息打印到屏幕上,将消息写入node的日志文件,并将消息写入rosout。
# rosout是一个方便的调试工具:你可以使用rqt_console提取消息,而不必查找带有node输出的控制台窗口。
rospy.loginfo("写出的数据:%s",msg.data)
rate.sleep()
2.2.订阅对象
对象获取:
class Subscriber(Topic):
"""
类注册为指定主题的订阅者,其中消息是给定类型的。
"""
def __init__(self, name, data_class, callback=None, callback_args=None,
queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False):
"""
Constructor.
@param name: 话题名称
@type name: str
@param data_class: 消息类型
@type data_class: L{Message} class
@param callback: 处理订阅到的消息的回调函数
@type callback: fn(msg, cb_args)
@param queue_size: 消息队列长度,超出长度时,头部的消息将被弃用
"""
2.3.服务对象
对象获取:
class Service(ServiceImpl):
"""
声明一个ROS服务
使用示例::
s = Service('getmapservice', GetMap, get_map_handler)
"""
def __init__(self, name, service_class, handler,
buff_size=DEFAULT_BUFF_SIZE, error_handler=None):
"""
@param name: 服务主题名称 ``str``
@param service_class:服务消息类型
@param handler: 回调函数,处理请求数据,并返回响应数据
@type handler: fn(req)->resp
"""
2.4.客户端对象
对象获取:
class ServiceProxy(_Service):
"""
创建一个ROS服务的句柄
示例用法::
add_two_ints = ServiceProxy('add_two_ints', AddTwoInts)
resp = add_two_ints(1, 2)
"""
def __init__(self, name, service_class, persistent=False, headers=None):
"""
ctor.
@param name: 服务主题名称
@type name: str
@param service_class: 服务消息类型
@type service_class: Service class
"""
请求发送函数:
def call(self, *args, **kwds):
"""
发送请求,返回值为响应数据
"""
等待服务函数:
def wait_for_service(service, timeout=None):
"""
调用该函数时,程序会处于阻塞状态直到服务可用
@param service: 被等待的服务话题名称
@type service: str
@param timeout: 超时时间
@type timeout: double|rospy.Duration
"""
3.回旋函数
def spin():
"""
进入循环处理回调
"""
理解:
def callback(data):
rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
rospy.Subscriber("chatter", String, callback)
rospy.spin()
这声明你的节点订阅了消息类型为std_msgs.msg.String的chatter话题。收到新消息时,将以消息(String)作为第一个参数来调用回调函数。rospy.spin()只是使节点无法退出,直到该节点已关闭。
4.时间
4.1.时刻
获取时刻,或是设置指定时刻:
#需求1:演示时间相关操作(获取当前时刻+设定指定时刻)
rospy.init_node("hello_time")
#获取时刻
right_now=rospy.Time.now() #将当前时刻(now函数被调用执行的那一刻)获取并封装成对象 参考的是1970.01.01 00.00
rospy.loginfo("当前时刻:%.2d", right_now.to_sec()) #sec函数,获取当前时刻距离参考时刻1970.01.01 00.00 过去多久
rospy.loginfo("当前时刻:%d", right_now.secs)
# 设置指定时间
time1=rospy.Time(100.5) #将时间(参考时间1970.01.01 00.00 逝去100.5秒)封装成Time对象、
time2=rospy.Time(100,35426) #两个参数,秒和纳秒
rospy.loginfo("指定时刻1:%.2f", time1.to_sec())
rospy.loginfo("指定时刻2:%.2f", time2.to_sec())
# 从某个时间值获取时间对象
time3=rospy.Time.from_sec(200.23)
rospy.loginfo("指定时刻3:%.2f", time3.to_sec())
4.2.持续时间
设置一个时间区间(间隔):
# 需求2:设置一个时间区间(间隔): 程序执行中停顿5秒
rospy.loginfo("休眠前-----------")
#1.封装一个持续时间对象(5s)
du=rospy.Duration(5,0)
# 2.再将持续时间休眠
rospy.sleep(du)
rospy.loginfo("休眠后--------------")
4.3.持续时间与时刻运算
为了方便使用,ROS中提供了时间与时刻的运算:
# 需求3:获取程序开始执行的时刻,且已知持续运行的时间,计算程序结束的时间
# 获取一个时刻
t1 = rospy.Time.now()
# 设置一个持续时刻
du1 = rospy.Duration(30)
# 结束时刻 t2=t1+du1
t2 = t1 + du1 # 注意时刻与时刻之间不能相加减 ,持续时间可以于持续时间想加减
rospy.loginfo("开始的时间:%.2d", t1.to_sec())
rospy.loginfo("结束的时间:%.2d", t2.to_sec())
4.4.设置运行频率
# 需求4:设置运行频率,可以保持一定速率进行循环
# rate对象可以允许你指定自循环的频率。它会追踪记录自上一次调用
# rate.sleep()后时间的流逝,并休眠直到一个频率周期的时间。参数为10时,我们期望每秒执行10次循环
rate = rospy.Rate(2)
while not rospy.is_shutdown:
rate.sleep()
rospy.loginfo("+++++++")
4.5定时器
ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:
#需求5: 创建定时器,实现类似于rospr.Rate()的功能(每隔某个时间间隔执行某种操作)(Timer)
""" Constructor.
@param period: desired period between callbacks 时间间隔
@type period: rospy.Duration
@param callback: callback to be called 回调函数
@type callback: function taking rospy.TimerEvent
@param oneshot: if True, fire only once, otherwise fire continuously until shutdown is called [default: False]
@type oneshot: bool
@param reset: if True, timer is reset when rostime moved backward. [default: False]
@type reset: bool
"""
timer=rospy.Timer(rospy.Duration(2),callback) #创建一个定时器 功能:每隔了时间间隔(2s)执行回调函数(callback)
# timer1=rospy.Timer(rospy.Duration(2),callback,oneshot=True) #只执行一次定时器。隔两秒执行callback,后关闭
rospy.spin() #有回调函数时不要忘记 spin()
回调函数:
def callback(event):
rospy.loginfo("--------")
rospy.loginfo("调用回调函数的时刻:%.2f", event.current_real.to_sec()) #envent 获取时刻
4.6.案例实现
#! /usr/bin/env python
# coding=UTF-8
import rospy
"""
需求1:
"""
def callback(event):
rospy.loginfo("------------")
rospy.loginfo("调用回调函数的时刻:%.2f", event.current_real.to_sec()) #envent 获取时刻
if __name__=="__main__":
#需求1:演示时间相关操作(获取当前时刻+设定指定时刻)
rospy.init_node("hello_time")
#获取时刻
right_now=rospy.Time.now() #将当前时刻(now函数被调用执行的那一刻)获取并封装成对象 参考的是1970.01.01 00.00
rospy.loginfo("当前时刻:%.2d", right_now.to_sec()) #sec函数,获取当前时刻距离参考时刻1970.01.01 00.00 过去多久
rospy.loginfo("当前时刻:%d", right_now.secs)
# 设置指定时间
time1=rospy.Time(100.5) #将时间(参考时间1970.01.01 00.00 逝去100.5秒)封装成Time对象、
time2=rospy.Time(100,35426) #两个参数,秒和纳秒
rospy.loginfo("指定时刻1:%.2f", time1.to_sec())
rospy.loginfo("指定时刻2:%.2f", time2.to_sec())
# 从某个时间值获取时间对象
time3=rospy.Time.from_sec(200.23)
rospy.loginfo("指定时刻3:%.2f", time3.to_sec())
# 需求2:设置一个时间区间(间隔): 程序执行中停顿5秒
rospy.loginfo("休眠前-----------")
#1.封装一个持续时间对象(5s)
du=rospy.Duration(5,0)
# 2.再将持续时间休眠
rospy.sleep(du)
rospy.loginfo("休眠后--------------")
# 需求3:获取程序开始执行的时刻,且已知持续运行的时间,计算程序结束的时间
# 获取一个时刻
t1 = rospy.Time.now()
# 设置一个持续时刻
du1 = rospy.Duration(30)
# 结束时刻 t2=t1+du1
t2 = t1 + du1 # 注意时刻与时刻之间不能相加减 ,持续时间可以于持续时间想加减
rospy.loginfo("开始的时间:%.2d", t1.to_sec())
rospy.loginfo("结束的时间:%.2d", t2.to_sec())
# # 需求4:设置运行频率,可以保持一定速率进行循环
# # rate对象可以允许你指定自循环的频率。它会追踪记录自上一次调用
# # rate.sleep()后时间的流逝,并休眠直到一个频率周期的时间。参数为10时,我们期望每秒执行10次循环
# rate = rospy.Rate(2)
# while not rospy.is_shutdown():
# rate.sleep()
# rospy.loginfo("+++++++")
#需求5: 创建定时器,实现类似于rospr.Rate()的功能(每隔某个时间间隔执行某种操作)(Timer)
""" Constructor.
@param period: desired period between callbacks 时间间隔
@type period: rospy.Duration
@param callback: callback to be called 回调函数
@type callback: function taking rospy.TimerEvent
@param oneshot: if True, fire only once, otherwise fire continuously until shutdown is called [default: False]
@type oneshot: bool
@param reset: if True, timer is reset when rostime moved backward. [default: False]
@type reset: bool
"""
timer=rospy.Timer(rospy.Duration(2),callback) #创建一个定时器 功能:每隔了时间间隔(2s)执行回调函数(callback)
# timer1=rospy.Timer(rospy.Duration(2),callback,oneshot=True) #只执行一次定时器。隔两秒执行callback,后关闭
rospy.spin() #有回调函数时不要忘记 spin()
5.其它函数
5.1.节点状态判断
def is_shutdown():
"""
@return: True 如果节点已经被关闭
@rtype: bool
"""
5.2.节点关闭函数
def signal_shutdown(reason):
"""
关闭节点
@param reason: 节点关闭的原因,是一个字符串
@type reason: str
"""
def on_shutdown(h):
"""
节点被关闭时调用的函数
@param h: 关闭时调用的回调函数,此函数无参
@type h: fn()
"""
#! /usr/bin/env python
# coding=UTF-8
"""
1.导包
2.初始化 ROS 节点:命名(唯一)
3.创建发布者对象
4.编写发布逻辑并发布数据
"""
# 导包
import rospy
from std_msgs.msg import String #消息类型 std_msgs.msg导入是为了使我们可以重用std_msgs/String消息类型进行发布
def callback():
rospy.loginfo("节点正在被关闭")
if __name__ == "__main__":
rospy.init_node("chenmo",anonymous=True) #传入节点名称
# 3.创建发布者对象
pub=rospy.Publisher("che",String,queue_size=10,latch=True) #话题名称,消息类型
# 4..编写发布逻辑并发布数据
# 创建数据
msg=String() #创建空的消息类型为String的消息,
msg_front="hellocehnmo"
count=0 #计数器
# 设置循环频率
rate=rospy.Rate(10)
# 循环体
while not rospy.is_shutdown():
count += 1
if count<10:
msg.data=msg_front+str(count)
# 发布数据 #发布对象: 前面创建的pub, 发布函数:.publish(),发布数据:前面创建的消息msg, 消息类型String
pub.publish(msg) #将字符串发布到chatter话题上
# 此循环调用rospy.loginf(), 它执行三项任务:将消息打印到屏幕上,将消息写入node的日志文件,并将消息写入rosout。
# rosout是一个方便的调试工具:你可以使用rqt_console提取消息,而不必查找带有node输出的控制台窗口。
rospy.loginfo("写出的数据:%s",msg.data)
else:
#关闭节点
rospy.on_shutdown(callback)
rospy.signal_shutdown("关闭节点")
rate.sleep()
5.3.日志函数
使用示例
#! /usr/bin/env python
# coding=UTF-8
import rospy
if __name__=="__main__":
# 演示日志函数
rospy.init_node("hello_log")
rospy.logdebug("DEBUG消息...")
rospy.loginfo("INFO消息...")
rospy.logwarn("WARN消息...")
rospy.logerr("ERROR消息...")
rospy.logfatal("FATAL消息...")
二.python文件导入
与C++类似的,在Python中导入其他模块时,也需要相关处理。
需求:首先新建一个Python文件A,再创建Python文件UseA,在UseA中导入A并调用A的实现。
实现:
- 新建两个Python文件,使用 import 实现导入关系;
- 添加可执行权限、编辑配置文件并执行UseA。
1.文件A 实现
#! /usr/bin/env python
# coding=UTF-8
num = 1000
2.UseA实现
#! /usr/bin/env python
# coding=UTF-8
import os
import sys
import rospy
"""
异常:直接调用tools_A时,ModuleNotFoundError:No module named 'toole_A'
原因:rosrun执行时默认的参考路径是工作空间下(demo02_ws),不是在scripts 下,在工作空间下无法查找依赖的模块
解决:可以声明python的环境变量,当依赖某个模块时,先去指定的环境变量中查找依赖
"""
# 设置临时环境变量,
# 但是路径写死了,影响可移植性
# sys.path.insert(0, path + "/home/chenmo/demo02_ws/src/plumbing_apis/scripys")
# 优化
path = os.path.abspath(".") # 动态获取tools.py的目录
rospy.loginfo(path)
sys.path.insert(0, path + "/src/plumbing_apis/scripts")
import tools_A
if __name__=="__main__":
rospy.init_node("node")
rospy.loginfo("num = %d", tools_A.num)
参考
https://zhuanlan.zhihu.com/p/440734846
http://www.autolabor.com.cn/book/ROSTutorials/di-3-zhang-ros-tong-xin-ji-zhi-jin-jie/31/314-shi-jian.htmlhttps://blog.csdn.net/qq_45152498/article/details/109880358