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
河田 卓志
마츠다 아키히로, 후쿠지 마사키, 유타니 테츠오
오일러리 재팬 발행
Reference
이 문제에 관하여(ROS 공부 제8탄: 조금 발전적인 액션), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/Yuya-Shimizu/items/9f54735dcdbafb69e719
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
#! /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() #ループに入り,到着すべきゴールが送られるのを待つ
#! /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}") #更新回数を表示
Reference
이 문제에 관하여(ROS 공부 제8탄: 조금 발전적인 액션), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/Yuya-Shimizu/items/9f54735dcdbafb69e719텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)