ROS2-动作

对于实际应用场景,我们希望使用一个指令控制机械臂抓起面前的方块。这种情况可以用话题,机械臂节点订阅该话题,我们向话题中发布消息,从而使机械臂移动。也可以使用服务,客户端向机械臂节点发送消息,机械臂作为服务端在确认收到消息后返回消息,告知消息已接收

但以上两种操作中,我们都无法得知机械臂具体进行了什么操作。于是动作的出现,完美解决了这个问题

动作的本质是在发送了请求后,接收端重复周期性返还数据,以告知消息发送者当前动作进行到了哪里,以此来实现对动作的全程跟踪。

动作接口

我们将消息发送者成为客户端,消息执行者成为服务端

在设计动作的接口中,我们需要考虑以下几点信息传输:

1、启动时发送消息,这是动作的开始阶段,由客户端发往服务端

2、结束时发送消息,这标志着动作执行完毕,由服务端发往客户端

3、反馈,这是动作过程中,由服务端发往客户端

由以上三个变量,ros2规定动作接口如下书写:注:文件后缀名为.action
 

#{启动消息}

---

#{结束消息}

---

#{反馈消息}

动作:从累加开始

为了实现最简单的动作,我们设计以下动作:由节点A发送消息给节点B一个int型,节点B执行int型次从0开始的自加,每次自加反馈给节点A消息,告知自加的进度,最终完成自加后返回节点A已经自加完毕的信息。

接口设计

int32 num
---
bool finish
---
int32 x

服务端设计

class PlusService(Node):
    def __init__(self,name):
        super().__init__(name)
        self.serv=ActionServer(self,Plus,'action_a',self.doPlus)
    def doPlus(self,goal):
        self.get_logger().info("get Request")
        feedback=Plus.Feedback()
        i=0
        for j in range(goal.request.num):
            i=i+1
            feedback.x=i
            goal.publish_feedback(feedback)
            time.sleep(1)
        goal.succeed()
        result=Plus.Result()
        result.finish=True
        return result
def main(args=None):
    rclpy.init(args=args)
    node=PlusService("Node_Plus_Service")
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

在服务端中,我们创建了一个节点,在节点中调用ros2的ActionServer函数创建了服务端实例,并声明了它的回调函数,每当收到请求后便会把请求作为参数执行回调函数。

信息的传递都需要借助接口方法创建反馈体对象、响应体对象,在doPlus方法中我们创建了feedback作为反馈,每次循环中i做自加,赋值feedback的x并调用publish_feedback传递给客户端

执行完毕循环后我们调用request中succeed方法来告知执行完毕,使用Result方法创建result类,赋值后返回给客户端。

客户端设计

客户端设计相对来说较为复杂

在客户端设计中,我们需要考虑如何发送请求、接收反馈的回调函数、接受最终结果的回调函数

至关重要的是,为了最终结果,我们需要在发送请求后追踪。咱们先来看代码吧!

class Plus_client(Node):
    def __init__(self,name):
        super().__init__(name)
        self.clienter=ActionClient(self,Plus,'action_a')
    def send(self,num):
        msg=Plus.Goal()
        msg.num=num
        self.clienter.wait_for_server()
        self.future=self.clienter.send_goal_async(msg,feedback_callback=self.feedbackCallback)
        self.future.add_done_callback(self.linkCallback)   #连接到服务器后的回调函数
    def feedbackCallback(self,msg):
        self.get_logger().info("Get message:%d" % msg.feedback.x)
    def doneCallback(self,msg):
        self.get_logger().info("End Plus!")
    def linkCallback(self,Result):
        doPlus=Result.result()
        if not doPlus.accepted:
            self.get_logger().info("Goal rejected")
            return
        self.get_logger().info("Goal Begin!")
        self.getResult=doPlus.get_result_async()
        self.getResult.add_done_callback(self.doneCallback)
def main(args=None):
    rclpy.init(args=args)
    node=Plus_client("ClientNode")
    node.send(5)
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

在这段程序中,调用逻辑相对来说较为复杂,我们梳理一下:

构造函数构造clienter类进行动作客户端实例

主函数调用send方法

send方法中声明msg作为请求体

调用clienter中的send_goal_async来进行发送数据,此处告知反馈的回调函数feedbackCallback并且把结果赋值给future

future中告知连接服务器的回调函数为linkCallback

在linkCallback函数执行时,说明已经完成服务器连接,把连接结果赋值给doPlus,此时判断指令是否被接受,若是,则调用doPlus实例中get_result_async方法,异步获取最终执行完毕的结果。同时将最后结束动作的回调函数设定为doneCallback

最终,结束时调用doneCallback。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值