2. 快速上手¶
roslibpy 上手非常简单。在接下来的例子中你将看到如何使用它与一个 ROS 环境相连接。
注解
在连接的过程中,确保在你网络中的 ROS 服务器配置并打开了 rosbridge server 和 TF2 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
的连接。有两个相关的方法可以实现这个事情:
roslibpy.Ros.close()
: 断开 websocket 连接. 在连接被关闭后,通过调用roslibpy.Ros.connect()
: 还可以再次连接。roslibpy.Ros.terminate()
: 终止主事件循环。如果连接还是打开着的,那么就会首先关闭它。注解
当使用
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