在Jetson Nano上学习ROS的记录(版本Ubuntu18.04,课程来源赵虚左老师的《ROS理论与实践》)第七章 ROS通信机制进阶(常用API、Python模块的导入)

系列文章目录

第一章 ROS空间创建、helloworld的实现、开启多个节点
第二章 话题通信
第三章 服务通信
第四章 参数服务器
第五章 常用指令
第六章 通信机制实操
第七章 ROS通信机制进阶(常用API、Python模块的导入)
第八章 元功能包、节点运行管理launch文件(teleop_twist安装方法)
第九章 重名问题、分布式通信

文章目录

  • 系列文章目录
  • 前言
  • 一、常用API
    • 1.API是什么
    • 2.初始化
    • 3.话题与服务相关对象
    • 4.回旋函数
    • 5.时间
    • 6.其他函数
  • 二、Python模块导入
  • 总结


前言

现在大二,之前大一有幸参加了2021的国赛,很壮烈的拿了个江苏赛区的二等奖。但发现无人机这个题,真的是往堆钱上走了。不上ROS不行,现在来记录一下一个纯小白学习ROS的过程和遇到的问题。防止学弟、学妹们再走我走过的弯路。板子用的是学长给的Jetson Nano(4GB),版本是Ubuntu18.04(已配置好基础ROS所需配置)。

一、常用API

1.API是什么

A:应用程序
P:开发编程
I:接口
API之主要目的是提供应用程序与开发人员以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。提供API所定义的功能的软件称作此API的实现。API是一种接口,故而是一种抽象。

2.初始化

  1. 建立功能包,并建立scripts文件夹,建立相关python文件,并给予权限。
    代码实例讲解如下:
#! /usr/bin/env python3
#encoding:utf-8

#1.导包 
import rospy
from std_msgs.msg import String   #发布的消息类型

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    """
    作用:ROS初始化
    参数:
        name:设置节点名称
        argv:封装节点调用时传递的函数
        anonymous:可以为节点名称生成随机后缀。可以解决重名问题
    使用:
        1.argv使用
        可以按照ROS中指定的语法格式,ROS可以解析并加以使用
        2.anonymous使用
        可以设置为true,节点名称会后缀随机数。可以开启多个节点。
    """
    rospy.init_node("sanDai")
    #3.实例化 发布者 对象
    pub = rospy.Publisher("che",String,queue_size=10) #话题名称,发布类型,容量是10
    #4.组织被发布的数据,并编写逻辑发布数据
    msg = String()  #创建 msg 对象
    rate = rospy.Rate(1)
    count = 0
    rospy.sleep(3 )#防止丢数据,因为创建需要时间。
    while not rospy.is_shutdown(): #判断这个节点是否关闭
        count +=1
        #拼接字符串
        msg.data = "hello"+ str(count)
        pub.publish(msg)
        rospy.loginfo("写出的数据:%s",msg.data)
        rate.sleep() #每隔1s执行一次循环
  1. cmakelists(162)
catkin_install_python(PROGRAMS
  scripts/demo01_apis_pub_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
  1. 新开一个命令行:roscore
    再开一个命令行:
cd myworld/
source ./devel/setup.bash
rosrun plumbing_apis demo01_apis_pub_p.py
  1. 若想实现argv传参(例如,在开启节点时,将参数A的值=10)
    可以rosrun plumbing_apis demo01_apis_pub_p.py _A:=10
    新开一个命令行,列出参数服务器里的参数:rosparam list
    响应结果:
/rosdistro
/roslaunch/uris/host_lzl_desktop__34755
/rosversion
/run_id
/sanDai/A

若想查找A的值:rosparam get /sanDai/A

3.话题与服务相关对象

  1. 建立功能包,并建立scripts文件夹,建立相关python文件,并给予权限。
    代码实例讲解如下:
#! /usr/bin/env python3
#encoding:utf-8

#1.导包 
import rospy
from std_msgs.msg import String   #发布的消息类型

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    """
    作用:ROS初始化
    参数:
        name:设置节点名称
        argv:封装节点调用时传递的函数
        anonymous:可以为节点名称生成随机后缀。可以解决重名问题
    使用:
        1.argv使用
        可以按照ROS中指定的语法格式,ROS可以解析并加以使用
        2.anonymous使用
        可以设置为true,节点名称会后缀随机数。可以开启多个节点。
    """
    rospy.init_node("sanDai",anonymous=True)
    #3.创建发布者对象
    """
    内容:latch
        bool值,默认是false
    作用:
        如果设置为True,可以将发布的最后一条数据保存,且后续
        当新的订阅对象连接时,会将该数据发送给订阅者
    使用:
        latch = True
    """
    pub = rospy.Publisher("che",String,queue_size=10,latch=True) #话题名称,发布类型,队列容量是10
    #4.编写发布逻辑并发布数据
    msg = String()  #创建数据
    rate = rospy.Rate(1)#指定发布频率
    count = 0#设置计数器
    rospy.sleep(3 )#防止丢数据,因为创建需要时间。
    while not rospy.is_shutdown(): #判断这个节点是否关闭
        count +=1
        #发布数据
        if count<=10:
            msg.data = "hello"+ str(count)
            pub.publish(msg)
            rospy.loginfo("写出的数据:%s",msg.data)
        rate.sleep() #每隔1s执行一次循环
  1. cmakelists(162)
catkin_install_python(PROGRAMS
  scripts/demo01_apis_pub_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
  1. 新开一个命令行:roscore
    再开一个命令行:
cd myworld/
source ./devel/setup.bash
rosrun plumbing_apis demo01_apis_pub_p.py

用命令行创建一个订阅者:rostopic echo /che #话题名

4.回旋函数

处理订阅消息时,需要使用到回调函数

def spin():
    """
    进入循环处理回调 
    """

5.时间

ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器…都与时间相关。

  1. 建立功能包,并建立scripts文件夹,建立相关python文件,并给予权限。
    代码实例讲解如下:
#! /usr/bin/env python3
#encoding:utf-8
import rospy
#rospy中已经导入了时间相关的包

"""
    需求1:演示时间相关操作(获取当前时刻+设置指定时刻)

    需求2:程序执行中停顿5s

    需求3:获取程序开始执行的时刻,且已知持续运行的时间,计算程序结束的时间

    需求4:创建定时器,实现类似于rospy.Rate的功能(隔某个时间间隔执行某种操作)


"""
def doMsg(event):
    rospy.loginfo("---------------------------------")
    rospy.loginfo("调用回调函数的时刻:%.2f",event.current_real.to_sec())#看参数event被调用时刻的秒数

if __name__ == "__main__":
    rospy.init_node("hello_time")#初始化节点

    # 需求1:演示时间相关操作(获取当前时刻+设置指定时刻)
        #获取时刻
    right_now =  rospy.Time.now()#获取于当前时刻(1.now函数被调用执行的那一刻,2.初始时间1970年01月01日00:00:00),并封装成对象,函数返回值为一个time对象
    rospy.loginfo("当前时刻:%.2f",right_now.to_sec())#距离当前时刻多少秒
    rospy.loginfo("当前时刻:%.2f",right_now.to_nsec())#距离当前时刻多少纳秒
        #设置指定
    time1 = rospy.Time(100)#将时间(距离初始时间过去100s)封装成time对象
    time2 = rospy.Time(100,312345678)#(secs=0, nsecs=0) 秒和纳秒会集成
    rospy.loginfo("指定时刻1:%.2f",time1.to_sec())
    rospy.loginfo("指定时刻2:%.2f",time2.to_sec())
        #从某个时间值获取时间对象
    time3 = rospy.Time.from_sec(200.12)
    rospy.loginfo("指定时刻3:%.2f",time3.to_sec())

    # 需求2:程序执行中停顿5s
    rospy.loginfo("休眠前----------------------------------------------------")
        #1.封装一个持续时间对象(5s)
    du = rospy.Duration(5,0) #参数(secs=0, nsecs=0)
        #2.再将持续时间休眠
    #rospy.sleep(du)
    rospy.loginfo("休眠后----------------------------------------------------")

    # 需求3:获取程序开始执行的时刻,且已知持续运行的时间,计算程序结束的时间
        #1.获取一个时刻t1
    t1 = rospy.Time.now()
        #2.设置一个持续时间du1
    du1 = rospy.Duration(5)
        #3.结束时刻 t2=t1+du1
    t2 = t1 + du1
    rospy.loginfo("开始时刻:%.2f",t1.to_sec())
    rospy.loginfo("结束时刻:%.2f",t2.to_sec())
    
    # 设置执行频率 每隔5s
    # rate = rospy.Rate(0.5)
    # while not rospy.is_shutdown():
    #     rate.sleep() #休眠
    #     rospy.loginfo("+++++++++++++++")

    # 需求4:创建定时器,实现类似于rospy.Rate的功能(隔某个时间间隔执行某种操作)
    timer = rospy.Timer(rospy.Duration(2),doMsg)#创建一个定时器对象(period:不同回调函数之间的时间间隔, callback:回调函数, oneshot=False:定时器是不是一次性的, reset=False)
    rospy.spin()#有回调函数必有spin

  1. cmakelists(162)
catkin_install_python(PROGRAMS
  scripts/demo01_apis_pub_p.py
  scripts/demo02_apis_time_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
  1. 新开一个命令行:roscore
    再开一个命令行:
cd myworld/
source ./devel/setup.bash
rosrun plumbing_apis demo02_apis_time_p.py

6.其他函数

在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常,而 python 中则通过 rospy.is_shutdown() 来实现判断,导致节点退出的原因主要有如下几种:

  • 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
  • 同名节点启动,导致现有节点退出;
  • 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())

另外,日志相关的函数也是极其常用的,在ROS中日志被划分成如下级别:

  • DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
  • INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
  • WARN(警告):提醒一些异常情况,但程序仍然可以执行;
  • ERROR(错误):提示错误信息,此类错误会影响程序运行;
  • FATAL(严重错误):此类错误将阻止节点继续运行
  1. 节点状态判断
    建立功能包,并建立scripts文件夹,建立相关python文件,并给予权限。
    代码实例讲解如下:
#! /usr/bin/env python3
#encoding:utf-8

#1.导包 
import rospy
from std_msgs.msg import String   #发布的消息类型

def cb():
    rospy.loginfo("节点正在被关闭---")

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    """
    作用:ROS初始化
    参数:
        name:设置节点名称
        argv:封装节点调用时传递的函数
        anonymous:可以为节点名称生成随机后缀。可以解决重名问题
    使用:
        1.argv使用
        可以按照ROS中指定的语法格式,ROS可以解析并加以使用
        2.anonymous使用
        可以设置为true,节点名称会后缀随机数。可以开启多个节点。
    """
    rospy.init_node("sanDai",anonymous=True)
    #3.创建发布者对象
    """
    内容:latch
        bool值,默认是false
    作用:
        如果设置为True,可以将发布的最后一条数据保存,且后续
        当新的订阅对象连接时,会将该数据发送给订阅者
    使用:
        latch = True
    """
    pub = rospy.Publisher("che",String,queue_size=10,latch=True) #话题名称,发布类型,队列容量是10
    #4.编写发布逻辑并发布数据
    msg = String()  #创建数据
    rate = rospy.Rate(1)#指定发布频率
    count = 0#设置计数器
    rospy.sleep(3 )#防止丢数据,因为创建需要时间。
    while not rospy.is_shutdown(): #判断这个节点是否关闭 如果节点沒关闭,返回False,如果关闭,返回True
        count +=1
        #发布数据
        if count<=10:
            msg.data = "hello"+ str(count)
            pub.publish(msg)
            rospy.loginfo("写出的数据:%s",msg.data)
        else:
            #关闭节点
            rospy.on_shutdown(cb)#在节点关闭过程中执行一个回调函数
            #rospy.spin()循环读取接收到数据,并调用回调函数处理,但是这边是只执行一次,故不用,否则将进入循环
            rospy.signal_shutdown("关闭节点")

        rate.sleep() #每隔1s执行一次循环

修改cmakelists(162)

catkin_install_python(PROGRAMS
  scripts/demo01_apis_pub_p.py
  scripts/demo02_apis_time_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

编译运行

新开一个命令行:roscore
再开一个命令行:

cd myworld/
source ./devel/setup.bash   
rosrun plumbing_apis demo01_apis_pub_p.py
  1. 日志
    建立功能包,并建立scripts文件夹,建立相关python文件,并给予权限。
    代码实例讲解如下:
#! /usr/bin/env python3
import rospy

if __name__ == "__main__":
    #演示日志函数
    rospy.init_node("hello_log")

    rospy.logdebug("DEBUG")
    rospy.loginfo("info")
    rospy.logwarn("警告")
    rospy.logerr("error")
    rospy.logfatal("致命的")

修改cmakelists(162)

catkin_install_python(PROGRAMS
  scripts/demo01_apis_pub_p.py
  scripts/demo02_apis_time_p.py
  scripts/demo03_apis_log_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

编译运行

新开一个命令行:roscore
再开一个命令行:

cd myworld/
source ./devel/setup.bash
rosrun plumbing_apis demo03_apis_log_p.py

二、Python模块导入

需求:首先新建一个Python文件A,再创建Python文件UseA,在UseA中导入A并调用A的实现。

实现:
新建两个Python文件,使用 import 实现导入关系;
添加可执行权限、编辑配置文件并执行UseA。

  1. 建立功能包,并建立scripts文件夹,建立相关python文件A/B,并给予权限。
    A代码实例讲解如下:
#! /usr/bin/env python3
num = 100

B代码实例讲解如下:

#! /usr/bin/env python3
#encoding:utf-8
"""
    需求: 实现基本的话题通信一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)

         PS: 二者需要设置相同的话题


    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号

    实现流程:
        1.导包 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 发布者 对象
        4.组织被发布的数据,并编写逻辑发布数据


"""
#1.导包 
from json import tool
import os
import rospy
import sys
from std_msgs.msg import String   #发布的消息类型

#设置临时环境变量
#sys.path.insert(0,"/home/lzl/myworld/src/plumbing_pub_sub/scripts")#路径写死,影响了代码到可移植性
#优化,可以动态获取路径
path = os.path.abspath(".")
sys.path.insert(0,path + "/src/plumbing_pub_sub/scripts")

import tools

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("talker_p")
    rospy.loginfo("num = %d",tools.num)
    #3.实例化 发布者 对象
    pub = rospy.Publisher("chatter",String,queue_size=10) #话题名称,发布类型,容量是10
    #4.组织被发布的数据,并编写逻辑发布数据
    msg = String()  #创建 msg 对象
    msg_front = "hello 你好"
    count = 0  #计数器 
    # 设置循环频率 1是1hz
    rate = rospy.Rate(1)
    rospy.sleep(3 )#防止丢数据,因为创建需要时间。
    while not rospy.is_shutdown(): #判断这个节点是否关闭

        #拼接字符串
        msg.data = msg_front + str(count)

        pub.publish(msg)
        rate.sleep() #每隔1s执行一次循环
        rospy.loginfo("写出的数据:%s",msg.data)
        count += 1

  1. cmakelists(162)
catkin_install_python(PROGRAMS
  scripts/demo01_pub_p.py
  scripts/demo02_sub_p.py
  scripts/demo03_pub_person_p.py
  scripts/demo04_sub_person_p.py
  scripts/tools.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
  1. 新开一个命令行:roscore
    再开一个命令行:
cd myworld/
source ./devel/setup.bash
rosrun plumbing_pub_sub demo01_pub_p.py

总结

以上就是今天要讲的内容,本文仅仅简单记录了ROS通信机制进阶(常用API、Python模块的导入),如果有问题请在博客下留言或者咨询邮箱:[email protected]

你可能感兴趣的:(ROS学习,python,ubuntu,linux,嵌入式硬件,单片机)