본문 바로가기
ROS_python_정리/msgs , srv , action

action fancy예제 _2 ( 비동기부분 포함 , srvs + msgs )

by 알 수 없는 사용자 2022. 4. 12.

코드

더보기

서버

#! /usr/bin/env python

import rospy

import time
import actionlib
from basics.msg import TimerAction, TimerGoal, TimerResult, TimerFeedback


def do_timer(goal):
    start_time = time.time()
    update_count = 0
    if goal.time_to_wait.to_sec() > 60.0:
        result = TimerResult()
        result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)
        result.updates_sent = update_count
        server.set_aborted(result, "Timer aborted due to too-long wait")
        return
    while (time.time() - start_time) < goal.time_to_wait.to_sec():
        if server.is_preempt_requested():
            result = TimerResult()
            result.time_elapsed = rospy.Duration.from_sec(
                    time.time() - start_time)
            result.updates_sent = update_count
            server.set_preempted(result, "Timer preempted")
            return
        feedback = TimerFeedback()
        feedback.time_elapsed = rospy.Duration.from_sec(
                time.time() - start_time)
        feedback.time_remaining = goal.time_to_wait - feedback.time_elapsed
        server.publish_feedback(feedback)
        update_count += 1
        time.sleep(1.0)
    result = TimerResult()
    result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)
    result.updates_sent = update_count
    server.set_succeeded(result, "Timer completed successfully")


rospy.init_node('timer_action_server')
server = actionlib.SimpleActionServer('timer', TimerAction, do_timer, False)
server.start()
rospy.spin()

 

클라이언트

#! /usr/bin/env python

import rospy

import time
import actionlib
from basics.msg import TimerAction, TimerGoal, TimerResult, TimerFeedback

#5->4->3->2->1-> status : 3 , result : 
def feedback_cb(feedback):
    print('[Feedback] Time elapsed: %f' % (feedback.time_elapsed.to_sec()))
    print('[Feedback] Time remaining: %f' % (feedback.time_remaining.to_sec()))


rospy.init_node('timer_action_client')
client = actionlib.SimpleActionClient('timer', TimerAction)
client.wait_for_server()
goal = TimerGoal()

# U can choose how to control this ( 1, 2, 3 )

#5->4->3->2->1  // status : 3 -> 5 , result : success
goal.time_to_wait = rospy.Duration.from_sec(5.0)
client.send_goal(goal, feedback_cb=feedback_cb)

#state : 4 , result : fail (cuz too long wait)
# Uncomment this line to test server-side abort:
#goal.time_to_wait = rospy.Duration.from_sec(500.0)
#client.send_goal(goal, feedback_cb=feedback_cb)

# 0             //status : 3 , result : sucess
# Uncomment these lines to test goal preemption:
#client.send_goal(goal, feedback_cb=feedback_cb)
#time.sleep(3.0)
#client.cancel_goal()

client.wait_for_result()
print('[Result] State: %d' % (client.get_state()))
print('[Result] Status: %s' % (client.get_goal_status_text()))
print('[Result] Time elapsed: %f' %
      (client.get_result().time_elapsed.to_sec()))
print('[Result] Updates sent: %d' % (client.get_result().updates_sent))

출력결과

마지막 프린트 [ Result ] 설명

state는 밑에 action 동작 상태 여부를 리턴 [ (client.get_state()) ]

status는 사실 사람이 직접 보내주는 메신저임 , 커스터마이징 [ (client.get_goal_status_text()) ]

time elapsed 는 이 action 토픽을 진행하는데 경과(elapsed)한 시간 [ (client.get_result().time_elapsed.to_sec()) ]

updates sent 는 feedback 몇번 했는 지 카운팅한거임 [ (client.get_result().updates_sent)) ]

0 정지

3 성공

4 실패 (cancel)

5 거절 (rejected)

6 선점 (client가 목표 추적을 멈추도록 요청할 때 )

 

 

여기서 4 ABORTED 와 6 PREEMPTING 의 차이

출처 : rosanswer

  • 4 ABORTED      : Cancel   : Stop processing goal(s) 
  • 6 PREEMPTING : Preempt : Stop processing current goal(s) in favor of new goal(s) given.

action 파일 ( simple_action_client,server.py 예제들이랑 동일한 액션토픽사용 )

"catkin_make" 할 시에 약간의 추가작업이 발생한다.

Timer.action 파일이 처리되어 메시지-정의 파일이 몇개 생성된다. ( 액션토픽을 관리하기 위한 메시지토픽들 )

ex) TimerAction.msg , TimerActionFeedback.msg , TimerActionGoal.msg , TimerActionResult.msg , TimerFeedback.msg , TimerGoal.msg , TimerResult.msg

이 메시지들은 ROS 토픽상에 만들어져 액션클라이언트/서버 프로토콜을 구현하는 데 사용된다. 메시지지만 클래쓰다.

 

 

다음 사진을 보면 이해하기 좋을듯

TimerGoal 메시지 토픽 ( 여기안엔 내가 선언한 자료형인 duration 형이 들어 있음 )

TimerActionGoal 메시지 토픽 ( 여기안에 내가 만든 액션토픽인 TimerGoal이 포함되있다 )

 

 

rostopic info 로 timer ( 액션을 구성하고 있는 토픽이 광고될 이름공간을 결정하는 서버이름 ) 의 액션토픽정보를 찍어보면 result는 퍼블리셔(server)가 , goal은 서브스크라이버(action)이 가지고 있다.

 

 


개인정리 요약본

[ ] <- 작업을 진행하는 주체

 

1.     [   server ]단에서 roscore에 올릴 이름공간과 액션자료형,콜백함수를 지정

2.     [    client ]단에서 roscore에 올릴 이름공간과 액션자료형 지정

3.     대기 [ client ]단에서 server에서 액션토픽 or 커넥트 올 때 까지 대기 ( 이름.wait_for_server )

4.     [ roscore ]단에서 server client 찾아냄 ( 이름공간 & 액션자료형 동일한거로 찾아냄 )

5.     [    client ]단에서 보낼 goal 데이터 넣어주고 servergoal액션토픽 전송

6.     대기 [ client ]단에서 server에서 액션토픽 or 커넥트 올 때 까지 대기 ( 이름.wait_for_result )

7.     From client to [ server ] 로 받은 goal 데이터로 콜백함수가 동작하는데, if 문은 조건부합 X, ‘5.0’goal 데이터가          들어가 있으므로 5while문 돈다.

8.     [   server ]단에서 보낼 result 데이터 넣어주고 clientresult액션 토픽 전송

9.     From server to [ client ] 로 받은 result 데이터로 프린트문(결과용) 찍은거임


서버

1~7 import 부분 

10~12 현재시간 구하는 함수 , update_count는 피드백 몇번 발행하는 지 확인용 변수다.

13~18 60초 이상일 경우 server 쪽에서 goal을 중단하고 client에 중단되었다고 메시지와 result를 보냄, 그 후 콜백함수 종료( return )

17 인자 (1) 실패 결과전송 (2)결과랑 무슨 에런지 str전송

19 client가 요청한 시간이 다되기전까지 실행되는 while 루프

20~26  client가 goal을 멈추(cancel)도록 요청하면 True를 반환 or

          두번째 클라이언트가 새로운 goal을 보낼때 도 True반환

          return 으로 콜백함수 종료

27~31 feedback 을 client쪽으로 전달함. client가 보낸 goal 을 참고해서 보냄 (  )

         TimerFeedback()자료형을 선언해서 time_elapsed 필드랑 time_remaining필드를 채운 후,

          server.publish_feedback(feedback) 으로 client단에 액션 토픽 전송

32 피드팩 카운터변수다. 피드백 얼마나 보냈는지 알기용 

33 1초랑 비슷하게 걍 타임슬립쓴거 야매다.

34~37 feedback 다 보내고 정해진 goal에 도달 한 후,  

40 전역 범위로 돌아가서 평상시처럼 노드를 초기화하고 명명함

41 fancy_action_server.py 라 TimerAction 로 지은거? , 인자를 보면 4개가 있다. (1) 액션을 구성하고 있는 토픽이 광고될 이름공간을 결정하는 서버이름이다. (2) 서버가 처리하는 액션의 자료형 TimerAction을 사용한다. (3) Goal 콜백이다. (4) 서버의 자동시작을 방지하기 위해서 False

42,43 액션서버 실행 후, Goal에 도달할 때까지 기다리는 rospy.spin() 루프사용

 

 

클라이언트

1~7 lib 삽입 , 7번라인 이부분이 개념이 헷갈림

10~12 콜백함수 코딩 , 디버깅처럼 중간중간 데이터를 프린팅

15 자기 이름 명명 roscore에 올릴껄?

16 client 객체 생성 , // 인자 : (1) 액션을 구성하고 있는 토픽이 광고될 이름공간을 결정하는 서버이름이다. (2) 서버가 처리하는 액션의 자료형 TimerAction을 사용한다.

17 액션 서버가 동작할 때까지 대기 ( service 토픽과 유사 ) , 액션서버의 동작여부는 다섯 개의 광고딘 토픽이 확인 될시 

18 TimerGoal 자료형객체 goal 생성 후

 

20~35 부분 goal구조체의 "time_to_wait" 필드에 '5.0'을 하드코딩으로 채움

                그 후 client에서 server로 goal메시지가 전달함 ( 객체.send_goal(goal) )

어떤 방식으로 하느냐에 따라 결과 값이 다른데 이거는 주석 참고하면 됨. ( 위에 결과창 사진 有 )

24 참고로 client.send_goal(goal, feedback_cb=feedback_cb) 여기서 인자를 설명해보면 (1) goal 로 보낼꺼

     (2) "feedback_cb(feedback)"를 콜백 함수로 지정. ---동작방식---> server로부터 feedback 데이터가 올 때 마다 feedback_cb 콜백함수 실행

 

37 server에서 " server.set_succeeded(result, "Timer completed successfully") " 이부분 실행 되면서 result가 오게 되고, 그런담에는 " client.wait_for_result() " 이부분에서 server의 Action동작을 감지하고 라인 넘어감

38~42 부분은 client 터미널에서 결과에 대한 부분을 정리해서 보여주기 위한 부분

 

 

 

'ROS_python_정리 > msgs , srv , action' 카테고리의 다른 글

ros1_srv  (0) 2022.05.27
ros1_msg  (0) 2022.04.18
action simple예제 _1 ( 비동기부분 제외 , service와 비슷함)  (0) 2022.04.11
action 정리  (0) 2022.04.10

댓글