承接上文,遇到了在动作通信开发中,使用rclpy编写代码进行feedback等操作,但所有逻辑均编写完后,却无法将goal_handle提交为succeed状态,之前的解决方案是更改自己重写的execute()函数名为my_execute()并且在提交SUCCEED前使用一次,将goal_handle的状态更改为EXECUTING状态,具体文章参考下述。
https://blog.csdn.net/m0_56210953/article/details/140221097
但这并非万全之策,眨眼一看确实没啥问题。但是首先,在我的任务进行中,执行结束日志打印之后还会再打印一次任务开始,我认为应该是最后调用execute后重新打开了一个线程,导致任务重新走了一遍,但是又不满足条件,于是仅仅打印了日志,这无伤大雅,但在逻辑上是不对的。
其次项目的业务逻辑是包括了中间取消的情况的,也就是强制ctrl+c取消任务,当我使用ctrl+c取消了客,户端的请求时,服务端依旧在为乌龟导航,且服务端也没有提示任务取消,经过代码检查之后发现并非是我代码的问题,熟悉的,当我使用了源码实现的函数来进行状态变更为CANCLED的时候,也出现了以下的两个错误
[exer04_action_server-2] rclpy._rclpy_pybind11.RCLError: Failed to update goal state: goal_handle attempted invalid transition from state ABORTED with event SUCCEED, at ./src/rcl_action/goal_handle.c:95
[exer04_action_server-2] rclpy._rclpy_pybind11.RCLError: Failed to update goal state: goal_handle attempted invalid transition from state ACCEPTEDwith event CANCLED, at ./src/rcl_action/goal_handle.c:95
还是状态变更的错误,但是Python并没有ACCEPT_AND_EXECUTE状态,本来一行代码能实现的问题扯了这么远,于是我尝试在子线程中变更我的回调函数为源码回调函数的参数,但是均失败了
# 原来的
Thread(target=self.my_execute, args=(goal_handle,)).start()
# 变更后的1(出错)
Thread(target=goal.execute, args=(self.my_execute,)).start()
# 变更后的2(出错)
Thread(target=goal.execute(self.goal_handle)).start()
看来不实现源码的功能是不行了,于是我掉头细细读了一下源码,发现其实是可以实现的,具体做法就是将状态转换的部分源码粘贴到自己的代码中,但是要注意的是不能粘贴到执行部分,一定是状态转换的部分源码
from rclpy.impl.implementation_singleton import rclpy_implementation as _rclpy
def my_execute(self, goal_handle):
# 在自己的执行函数中粘贴状态转换语句
goal_handle._update_state((_rclpy.GoalEvent.EXECUTE))
这样其实之前更改的最后的goal_handle.execute()也可以删掉了,毕竟已经把状态在之前就进行了变更。这样问题就解决了