ROS学习经历(6)
在上一节,我们创建了对应的服务接口,现在让我们来创建服务吧。
1. 如何编写一个Python服务
步骤如下:
服务端:
- 导入服务接口
- 创建服务端回调函数
- 声明并创建服务端
- 编写回调函数逻辑处理请求
2. 编写服务端李四代码
首先打开我们的town_ws
工作区。
2.1 导入服务接口
1)添加依赖,ament_python
只需要在package.xml中加入代码:
<depend>village_interfaces</depend>
2)程序中导入,在程序开头加入代码:
#从村庄接口服务类中导入借钱服务
from village_interfaces.srv import BorrowMoney
2.2 创建服务端并定义服务回调函数
1)由于面向对象的继承特性,WriteNode
继承了Node
的属性,所以我们可以在当中的__init__
函数中创建成员变量borrow_server
。
# 新建借钱服务
self.borrow_server = self.create_service(BorrowMoney, "borrow_money", self.borrow_money_callback)
传入的三个参数:
- 服务接口类型,
BorrowMoney
,上一章创建的 - 服务名称,
"borrow_money"
,具有唯一性,是我们自己创建的 - 回调函数,
self.borrow_money_callback
,我们下一步定义
2.3 定义回调函数
def borrow_money_callback(self,request, response):
"""
借钱回调函数
参数:request 客户端请求对象,携带着来自客户端的数据
response 服务端响应,返回服务端的处理结果
返回值:response
"""
return response
这个函数有三个入口参数:
- request是客户端的请求对象,带着客户端的数据
其结构就是服务接口中定义的name
和money
- reponse是服务端响应,返回服务端的处理结果
其结构由success
和money
组成
2.4 编写回调函数
代码并不复杂,理解一下就可以了
def borrow_money_callback(self,request, response):
"""
借钱回调函数
参数:request 客户端请求
response 服务端响应
返回值:response
"""
self.get_logger().info("收到来自: %s 的借钱请求,目前账户内还有%d元" % (request.name, self.account))
#根据李四借钱规则,借出去的钱不能多于自己所有钱的十分之一,不然就不借
if request.money <= int(self.account*0.1):
response.success = True
response.money = request.money
self.account = self.account - request.money
self.get_logger().info("借钱成功,借出%d 元 ,目前账户余额%d 元" % (response.money,self.account))
else:
response.success = False
response.money = 0
self.get_logger().info("对不起兄弟,手头紧,不能借给你")
return response
2.5 编写服务端代码
1)编译
colcon build --packages-select village_li
2)启动并查看服务列表
source install/setup.bash
ros2 run village_li li4_node
3)手动借钱
source install/setup.bash
ros2 service call /borrow_money village_interfaces/srv/BorrowMoney "{name: 'li3', money: 5}"
4)再借50块钱,观测效果
ros2 service call /borrow_money village_interfaces/srv/BorrowMoney "{name: 'li3', money: 50}"
3 创建客户端
让我们再创建一个节点li3来作为客户端吧,首先按照前面创建一个节点。
编写服务通信的客户端的一般步骤:
- 导入服务接口
- 创建请求结果接受回调函数
- 声明并创建客户端
- 编写结果接受逻辑
- 调用客户端发送请求
3.1 导入服务接口
1)修改package.xml,但李三和李四在用一个功能包中,所以不需要再修改
2)导入接口
from village_interfaces.srv import BorrowMoney
3.2 创建请求结果接受回调函数
def borrow_respoonse_callback(self,response):
"""
借钱结果回调
"""
pass
只有一个入口response
3.3 创建客户端
#在__init__函数中创建一个服务的客户端
self.borrow_money_client_ = self.create_client(BorrowMoney, "borrow_money")
函数中有两个入口参数,一个是服务接口类型,一个是服务名称
这里的两个参数需要和服务端的完全一致,才可通信。这点要特别注意。
3.4 编写回调函数处理逻辑
根据结果反应
def borrow_respoonse_callback(self,response):
"""
借钱结果回调
"""
# 打印一下信息
result = response.result()
if result.success == True:
self.get_logger().info("果然是亲弟弟,借到%d,吃麻辣烫去了" % result.money)
else:
self.get_logger().info("害,连几块钱都不借,我还是不是他亲哥了,世态炎凉呀")
3.5 创建发送请求函数
我们在li3中编写一个函数用于创建发送的数据,并请求发送。
def borrow_money_eat(self):
"""
借钱吃麻辣烫函数
"""
#打印一句话
self.get_logger().info("找我弟借钱吃麻辣烫喽")
#等待服务启动,每1s检查一次,如果服务没有启动,则一直循环
while not self.borrow_money_client_.wait_for_service(1.0):
self.get_logger().warn("我弟不在线,我再等等。")
# 构建请求内容
request = BorrowMoney.Request()
#将当前节点名称作为借钱者姓名
request.name = self.get_name()
#借钱金额10元
request.money = 10
#发送异步借钱请求,借钱成功后就调用borrow_respoonse_callback()函数
self.borrow_money_client_.call_async(request).add_done_callback(self.borrow_respoonse_callback)
核心部分在call_async(request).add_done_callback
,用于发送请求,并且添加了一个任务完成时的回调函数
3.6 在main中调用发送函数
加入一行代码
node.borrow_money_eat() #增加一行,李三借钱
这时的main的代码内容如下:
def main(args=None):
"""
ros2运行该节点的入口函数,可配置函数名称
"""
rclpy.init(args=args) # 初始化rclpy
node = BaiPiaoNode() # 新建一个节点
node.borrow_money_eat() #增加一行,李三借钱
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # rcl关闭
3.7 测试
1)编译功能包
colcon build --packages-select village_li
2)运行li3节点
source install/setup.bash
ros2 run village_li li3_node
3)运行li4节点
source install/setup.bash
ros2 run village_li li4_node
4)运行wang2节点来买小说
source install/setup.bash
ros2 run village_wang wang2_node
5)再运行li3节点
ros2 run li3_node