2. 快速上手

roslibpy 上手非常简单。在接下来的例子中你将看到如何使用它与一个 ROS 环境相连接。

注解

在连接的过程中,确保在你网络中的 ROS 服务器配置并打开了 rosbridge serverTF2 web republisher(具体请戳 ROS 设置

这些例子假定了 ROS 服务器运行在同一台主机上。对于不在同一台主机的情况,只需要将host参数从'localhost'改为 ROS master 的 IP 地址

注解

port参数必须设定为9090,因为rosbridge的默认端口号是9090。如果想改变端口号,可参考这里

2.1. 第一个例子

用以下命令导入roslibpy:

>>> import roslibpy

用以下命令初始化连接:

>>> ros = roslibpy.Ros(host='localhost', port=9090)
>>> ros.run()

是不是很简单?

让我们检查一下连接状态:

>>> ros.is_connected
True

耶( •̀ ω •́ )y 我们的第一个例子成功跑通!

2.2. 融会贯通

让我们以 Python 文件的形式建立一个完整的例子。

新建一个文件并命名为ros-hello-world.py,然后复制粘贴以下内容:

import roslibpy

client = roslibpy.Ros(host='localhost', port=9090)
client.run()
print('Is ROS connected?', client.is_connected)
client.terminate()

在命令行中输入以下命令运行该程序:

$ python ros-hello-world.py

这个程序运行起来之后,会尝试建立与 ROS 的连接,连接建立后打印输出信息,并终止连接。

2.3. 控制事件循环

在之前的例子里,我们通过调用run()来开启与 ROS 的连接,这样会在后台开启一个事件循环。在某些情况下,我们希望在前台更明确地处理主事件循环,roslibpy.Ros 提供了一个run_forever()方法来做这件事。

如果我们如果使用这个方法启动事件循环,则需要事先设置好所有连接处理配置。我们使用roslibpy.Ros.on_ready()来做这件事。

接下来的代码片段用run_forever()on_ready()实现了与之前的例子同样的功能:

from __future__ import print_function
import roslibpy

client = roslibpy.Ros(host='localhost', port=9090)
client.on_ready(lambda: print('Is ROS connected?', client.is_connected))
client.run_forever()

注解

run()run_forever()的区别在于,前者新开一个单独的线程来处理事件,而后者会阻塞当前线程。

2.4. 断开连接

在你的任务完成后,你应该干净地断开与 rosbridge 的连接。有两个相关的方法可以实现这个事情:

注解

当使用 twisted/authbahn 循环的时候,终止主事件循环是一个不可逆的行为,因为 twisted 反应器 (reacters) 不能被重启。这个操作应该保留到你程序的最后面再使用。

2.5. 重新连接

如果在开启连接的时候 rosbridge 不响应,或者一个已建立的连接意外断开的时候, roslibpy 将会自动尝试重新连接,并且重新连接话题的订阅者和发布者。重新连接尝试时间的间隔是指数递减的。

2.6. Hello World: 话题(Topics)

ROS 中的Hello World例子是开启两个节点,并利用话题的订阅/发布来建立通讯。这两个节点(一个 talker 和一个 listener)非常简单,但是透过它们可以便于理解在 ROS 框架下的分布式系统中,两个进程之间的通信是如何工作的。

接下来,我们用 roslibpy 来建立一个简单的话题通讯。

2.6.1. 一个 talker 节点

接下来的例子是开启一个 ROS 节点并循环发布信息(按下Ctrl+C来终止)。

import time

import roslibpy

client = roslibpy.Ros(host='localhost', port=9090)
client.run()

talker = roslibpy.Topic(client, '/chatter', 'std_msgs/String')

while client.is_connected:
    talker.publish(roslibpy.Message({'data': 'Hello World!'}))
    print('Sending message...')
    time.sleep(1)

talker.unadvertise()

client.terminate()

注解

这里以 ROS 中的 std_msgs/String 消息类型为例,其它消息类型也可以利用 Python 字典的方式构建,参考这个例子

2.6.2. 一个 listener 节点

Listener 端的代码如下:

from __future__ import print_function
import roslibpy

client = roslibpy.Ros(host='localhost', port=9090)
client.run()

listener = roslibpy.Topic(client, '/chatter', 'std_msgs/String')
listener.subscribe(lambda message: print('Heard talking: ' + message['data']))

try:
    while True:
        pass
except KeyboardInterrupt:
    client.terminate()

2.6.3. 运行例程

打开一个终端,开启 talker 节点:

$ python ros-hello-world-talker.py

打开另一个终端,开启 listener 节点:

$ python ros-hello-world-listener.py

注解

两个文件的位置不必在一起,它们可以在不同的路径、甚至不同的计算机中,只要保证是同一个 Ros master 即可。

2.7. 使用服务(Services)

节点之间的另一种通讯方式是通过 ROS 服务来进行。

服务一般需要定义请求和回应的类型,为了简单,下面的例子使用了现成的get_loggers服务:

import roslibpy

client = roslibpy.Ros(host='localhost', port=9090)
client.run()

service = roslibpy.Service(client, '/rosout/get_loggers', 'roscpp/GetLoggers')
request = roslibpy.ServiceRequest()

print('Calling service...')
result = service.call(request)
print('Service response: {}'.format(result['loggers']))

client.terminate()

2.8. 创建服务(Services)

只要服务类型的定义存在于您的 ROS 环境中,就可以创建新服务。

下面的例子展示了如何创建一个简单的服务,它使用ROS 中定义的标准服务类型之一(std_srvs/SetBool):

import roslibpy

def handler(request, response):
    print('Setting speed to {}'.format(request['data']))
    response['success'] = True
    return True

client = roslibpy.Ros(host='localhost', port=9090)

service = roslibpy.Service(client, '/set_ludicrous_speed', 'std_srvs/SetBool')
service.advertise(handler)
print('Service advertised.')

client.run_forever()
client.terminate()

下载该脚本,并输入如下命令:

$ python ros-service.py

程序开始运行,期间服务会一直处于活动状态(按下Ctrl+C来终止)。

不要关闭这个服务,下载并运行以下代码示例来调用服务,以验证服务是否正常工作:

下载后在一个新的终端中键入以下命令:

$ python ros-service-call-set-bool.py

注解

现在您已经掌握了 roslibpy 的基础知识,更多细节请查看 API 文档

2.9. Actions

除了话题和服务之外,ROS还提供 Actions,它们被用于长时间运行的任务,比如导航,因为它们是非阻塞的,并且允许任务执行时被抢占和取消。

roslibpy 既支持 action 客户端,也可以通过 roslibpy.actionlib.SimpleActionServer提供 action 服务器。

下面的例子使用斐波那契 action,该 action 的定义可在 actionlib_tutorials 中查看。

2.9.1. Action 服务器

让我们从斐波那契服务器的定义开始:

import roslibpy
import roslibpy.actionlib

client = roslibpy.Ros(host='localhost', port=9090)
server = roslibpy.actionlib.SimpleActionServer(client, '/fibonacci', 'actionlib_tutorials/FibonacciAction')

def execute(goal):
    print('Received new fibonacci goal: {}'.format(goal['order']))

    seq = [0, 1]

    for i in range(1, goal['order']):
        if server.is_preempt_requested():
            server.set_preempted()
            return

        seq.append(seq[i] + seq[i - 1])
        server.send_feedback({'sequence': seq})

    server.set_succeeded({'sequence': seq})


server.start(execute)
client.run_forever()

下载后键入以下命令:

$ python ros-action-server.py

在程序运行时,action 服务器将保持活动状态(按下Ctrl+C来终止)。

如果您想在下一个示例中测试这个窗口,请不要关闭它。

2.9.2. Action 客户端

现在,让我们看看如何为新创建的服务器编写一个 action 客户端。

下面的程序显示了一个简单的 action 客户端:

from __future__ import print_function
import roslibpy
import roslibpy.actionlib

client = roslibpy.Ros(host='localhost', port=9090)
client.run()

action_client = roslibpy.actionlib.ActionClient(client,
                                                '/fibonacci',
                                                'actionlib_tutorials/FibonacciAction')

goal = roslibpy.actionlib.Goal(action_client,
                               roslibpy.Message({'order': 8}))

goal.on('feedback', lambda f: print(f['sequence']))
goal.send()
result = goal.wait(10)
action_client.dispose()

print('Result: {}'.format(result['sequence']))

下载后键入以下命令:

$ python ros-action-client.py

您将立即看到我们的 action 服务器的所有中间计算,并在最后一行显示生成的斐波那契数列。

这个例子非常简单,使用了 roslibpy.actionlib.Goal.wait() 函数,以使代码更易于阅读。一个更鲁棒的处理方法是使用回调把结果连接到 result 事件。

2.10. 查询 ROS API

ROS 提供了一系列 API 用来查询话题、服务、节点等。这些 API 可以用 Python 代码以编程的方式使用,也可以通过命令行调用。

2.10.1. 通过命令行使用

命令行的方式模仿了 ROS 本身的调用过程。

下列的命令都是可用的:

$ roslibpy topic list
$ roslibpy topic type /rosout
$ roslibpy topic find std_msgs/Int32
$ roslibpy msg info rosgraph_msgs/Log

$ roslibpy service list
$ roslibpy service type /rosout/get_loggers
$ roslibpy service find roscpp/GetLoggers
$ roslibpy srv info roscpp/GetLoggers

$ roslibpy param list
$ roslibpy param set /foo "[\"1\", 1, 1.0]"
$ roslibpy param get /foo
$ roslibpy param delete /foo

2.11. 进阶例程

下列是一些更复杂的使用 roslibpy 的例子。

我们鼓励大家通过 pull request 或 issue tracker 来提交更多的例程。