ROS 공부 제8탄: 조금 발전적인 액션

#프로그래밍 ROS< 조금 발전적인 액션 >



소개



하나의 참고서에 따라 ROS(Robot Operating System)를 어려움 없이 다루는 것이 목적이다. 제 8 탄으로서 조금 발전적인 액션을 다룬다.

환경



가상 환경





소프트
VMware Workstation 15

실장 RAM
2GB

OS
우분투 64비트

ISO 파일
ubuntu-mate-20.04.1-desktop-amd64.iso



컴퓨터





장치
MSI

프로세서
Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz 2.50GHz

실장 RAM
8.00GB(7.89GB 사용 가능)

OS
Windows (Windows 10 Home, 버전: 1909)



ROS





Distribution
noetic

프로그래밍 언어
파이썬 3.8.5


기본 액션과의 차이



나중에 소스 코드는 보여 주지만, 여기서 취급하는 액션에서는 피드백의 기능이나 강제 종료, 처리 중단과 같은 액션의 특유라고도 할 수 있는 것까지 다루고, 전회보다 좀 더 발전한 타이머의 작성을 예로서 실시한다.

구현



이번에는 서버와 클라이언트를 모두 보여주기 때문에 두 가지 소스 코드가 있습니다. 소스 코드 내에서 프로그램 설명은 상세히 설명된다.

소스 코드 ①(서버)

fancy_action_server.py
#! /usr/bin/env python3

import rospy

import time         #タイマー作成時に必要となる
import actionlib    #SimpleActionServerクラスを提供 → アクションのサーバー作成に必要と解釈
from action_lesson.msg import TimerAction, TimerGoal, TimerResult, TimerFeedback    #Timer.actionファイルから自動生成したメッセージクラスで必要なものを読み込む


def do_timer(goal):
    """新しいゴール(目標)を受け取ったときに呼び出される関数"""

    start_time = time.time()
    updates_count = 0           #更新回数を格納する変数を0で初期化

    #60秒を超えるタイマーは使ってほしくない → 打ち切る(強制終了)
    if goal.time_to_wait.to_sec() > 60:
        """タイマーに制限を持たせるための処理"""
        result = TimerResult()  #TimerResultの型のリザルトメッセージ(result)を作成 ←これを作成しないと,定義ファイル内のリザルトの部分にアクセスできない
        result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time) #「現在時刻-開始時刻」をDuration(ROSで扱う期間)に変換してtime_elapsedフィールドに代入
        result.updates_sent = updates_count     #更新回数を記録
        server.set_aborted(result, "Timer aborted due to too-long wait")    #set_aborted()関数により,強制終了
        return

    #一度にsleepを使って目標時間の経過を待つのでなくwhileを使って刻むように時間を経過させることで,途中干渉を可能としている
    while (time.time() - start_time) < goal.time_to_wait.to_sec():

        if server.is_preempt_requested():
            """中断リクエストに対応する処理"""
            result = TimerResult()  #TimerResultの型のリザルトメッセージ(result)を作成 ←これを作成しないと,定義ファイル内のリザルトの部分にアクセスできない
            result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time) #「現在時刻-開始時刻」をDuration(ROSで扱う期間)に変換してtime_elapsedフィールドに代入
            result.updates_sent = updates_count     #更新回数を記録
            server.set_preempted(result, "Timer preempted") #set_preempted()関数により,強制終了(リクエストがあれば中断)
            return

        #状況をfeedbackとして記録
        feedback = TimerFeedback()  #TimerFeedbackの型のフィードバックメッセージ(feedback)を作成 ←これを作成しないと,定義ファイル内のフィードバックの部分にアクセスできない
        feedback.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)   #「現在時刻-開始時刻」をDuration(ROSで扱う期間)に変換してフィードバックのtime_elapsedフィールドに代入
        feedback.time_remaining = goal.time_to_wait - feedback.time_elapsed  #目標時間 - 経過時間で残り時間を計算し,フィードバックのtime_remainingに格納
        server.publish_feedback(feedback)   #クライアントに送る
        updates_count += 1  #更新回数を加算

        time.sleep(1.0) #1.0秒刻み(本当のタイマーとしては正確ではないが,あくまでもテスト)    

    result = TimerResult()  #TimerResultの型のリザルトメッセージ(result)を作成 ←これを作成しないと,定義ファイル内のリザルトの部分にアクセスできない
    result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time) #「現在時刻-開始時刻」をDuration(ROSで扱う期間)に変換してtime_elapsedフィールドに代入
    result.updates_sent = updates_count     #今回は0として,更新回数は考えないこととする.
    server.set_succeeded(result, "Timer completed successsfully")   #resultを引数として,set_succeeded()関数を呼び出すことで,SimpleActionServerにゴールしたことを伝える.


rospy.init_node('timer_action_server')  #ノードの初期化

server = actionlib.SimpleActionServer('timer', TimerAction, do_timer, False)    #'timer'という名で,TimerActionというアクションの型 do_timerを実行.サーバーの自動起動を無効にするためFalseを指定

server.start()  #サーバー生成後,明示的にサーバーを開始する

rospy.spin()    #ループに入り,到着すべきゴールが送られるのを待つ

이전에 취급 한 액션 서버는 처리 내용 (do_timer 함수) 만 변경되어 서버 조립 자체의 변화가 없음을 알 수 있습니다.

소스 코드②(클라이언트)

fancy_action_client.py
#! /usr/bin/env python3

import rospy

import time
import actionlib #SimpleActionClientを使うためのパッケージ
from action_lesson.msg import TimerAction, TimerGoal, TimerResult, TimerFeedback    #アクションにアクセスするためのクラス


def feedback_cb(feedback):
    """feedbackのcallback関数"""
    print(f"[Feedback] Time elapsed: {feedback.time_elapsed.to_sec()}") #経過時間の表示
    print(f"[Feedback] Time remaining: {feedback.time_remaining.to_sec()}") #残り時間の表示



rospy.init_node('timer_action_client') #ノードの初期化

#actionlibパッケージにあるSimpleActionClientクラスを使う(アクション利用の最も簡単な方法)
client = actionlib.SimpleActionClient('timer', TimerAction) #サーバー名,型ともに利用するサーバーっと一致させる必要がある

client.wait_for_server()    #サーバーの応答を待つ

goal = TimerGoal()  #goalという変数にTimerGoalのインスタンスを生成(TimerGoalの値にアクセス可能となる)
goal.time_to_wait = rospy.Duration.from_sec(5.0)    #5.0秒をタイマーにセットrospy.Duration.from_secでrosでの期間単位Durationに変換
#goal.time_to_wait = rospy.Duration.from_sec(500.0) #500.0秒をタイマーにセット→60秒を超えているので強制終了される

client.send_goal(goal, feedback_cb = feedback_cb)   #clientとしてgoalをサーバーに送ると同時にfeedback_cb関数を呼び出す

#time.sleep(3.0)    #3秒待つ
#client.cancel_goal()   #中断のリクエストはclient.cancel_goal()で行う

client.wait_for_result()    #サーバーからの結果を待つ

State = client.get_state()                                  #ゴールの状態を数値で取得(PREEMPTED=2, SUCCEEDED=3, ABORTED=4)
Status = client.get_goal_status_text()                      #サーバーで結果を送る際に第2引数に指定した文字列を取得
Time_elapsed = client.get_result().time_elapsed.to_sec()    #取得した結果の経過時間を秒単位に変換してresultに格納
Updates = client.get_result().updates_sent                  #更新回数を取得

print(f"[Result] State: {State}")           #ゴールの状態を表示
print(f"[Result] Status: {Status}")         #ゴールの状況報告を表示
print(f"[Result] Time elapsed: {Time_elapsed}") #経過時間を表示
print(f"[Result] Updates sent: {Updates}")  #更新回数を表示

클라이언트도 서버처럼 기본적인 클라이언트 구성은 크게 변하지 않지만, 서버에서 여러가지 대응할 수 있게 된 만큼, 클라이언트도 여러가지로 할 수 있게 되었다.

결과



서버에서는 타이머를 제공하고 있지만, 결정적으로 60초를 넘는 목표 시간은 설정할 수 없게 하고 있다. 또, 도중에 강제 중단의 요청이 들어왔을 때에는 받아들이도록 하고 있다. 따라서 클라이언트가받는 결과는 "정상 작동", "강제 종료", "강제 중단"의 세 가지 패턴입니다. 이하에 그 때의 모습을 각각 나타낸다.

정상 가동


피드백의 효과로 순차 상황보고가 이루어지고있다.

강제 종료


원래 목표 시간이 규정외이면 타이머는 작동하지 않게 되어 있다.

강제 중단


여기서는 3 초 후 중단 요청을 서버에 보내므로 3 초 경과 후에 타이머가 종료하고 그 때의 결과를 출력하고 있음을 알 수 있습니다.

감상



이번에 ROS의 기초 중의 기초라고도 할 수 있는 주제, 서비스, 액션에 대해 배웠다. 많이 익숙해 졌다고 생각합니다. 이번 내용도 많이 이해하기 쉬웠다. 다음 번부터는 이러한 도구를 사용하여 드디어 실천적인 공부에 임할 것 같아 기대된다.

참고문헌



프로그래밍 ROS Python으로 로봇 애플리케이션 개발
      Morgan Quigley, Brian Gerkey, William D.Smart
                   河田 卓志
         마츠다 아키히로, 후쿠지 마사키, 유타니 테츠오
               오일러리 재팬 발행

좋은 웹페이지 즐겨찾기