ROS入門 - アクションプログラミング

ROS入門 - アクションプログラミング

クライアントは移動するターゲットを送信して、サーバーとクライアントのコード実装を含む、ロボットがターゲット位置に移動するプロセスをシミュレートし、リアルタイムの位置フィードバックを必要とします

アクションとは何ですか?

ロボットは複雑な知能システムです. キーボードのリモコンの動きや特定のターゲットの認識などの単純なものではありません. 私たちが実現する必要があるのは、食品の配達、配達、仕分けなどの特定のシナリオのニーズを満たすロボットです. .

これらのアプリケーション機能の実現では、別の ROS 通信メカニズム、つまりアクションがよく使用されます。この概念の意味は名前からもよく理解できますが、この通信メカニズムの目的は、ロボットの完全な動作のプロセスの管理を容易にすることです。

アクションは、次の 4 つのポイントに大まかに要約できます。

  • 質疑応答のコミュニケーションメカニズム

  • 継続的なフィードバックで

  • タスクの途中で中止可能

  • ROSに基づくメッセージメカニズムの実装

通信模型

たとえば、ロボットを円を描くように回転させたい場合、これは 1 回のクリックでできることではありません. ロボットは 360 度で終わるまで少しずつ回転する必要があります. ロボットが前にいないとします.ロボットが円を描き始めたのか、どこに行ったのか知っていますか?

OK, 今必要なのはフィードバックです. たとえば 1 秒ごとに, 10 度, 20 度, 30 度と何度回転したかを教えてください. しばらくして 360 度に達したら、別のメッセージを送信します.アクションが完了したことを示す

一定期間実行する必要があるこのような動作の場合、進行状況バーをインストールするのと同じように、アクションの通信メカニズムを使用する方が適切です。いつでも進行状況を制御できます。進行中、いつでも移動をキャンセルするコマンドを送信することもできます

アクションメッセージを定義するには?

ここでは、新しい関数パッケージを作成します。

catkin_create_pkg learning_action roscpp rospy std_msgs geometry_msgs turtlesim message_generation actionlib_msgs actionlib

関数パッケージ ディレクトリの下に新しいフォルダーを作成し、action という名前を付けて、このフォルダーに空のファイル TurtleMove.action を作成します。

Motion.action ファイルでアクション メッセージの内容を定義します。

# Define the goal
float64 turtle_target_x  # Specify Turtle's target position
float64 turtle_target_y
float64 turtle_target_theta
---
# Define the result
float64 turtle_final_x
float64 turtle_final_y
float64 turtle_final_theta
---
# Define a feedback message
float64 present_turtle_x
float64 present_turtle_y
float64 present_turtle_theta

アクション (アクション) 通信インターフェースは、ゴール、キャンセル、ステータス、フィードバック、結果の 5 つのメッセージ定義を提供します。

  • 目標: リリース タスクの目標

  • cancel: タスクのキャンセル要求

  • status: クライアントに現在のステータスを通知します

  • feedback: モニタリング データを実行する定期的なフィードバック タスク

  • result: タスクの実行結果をクライアントに送信します。一度だけ公開します

而 .action 文件用来定义其中三种消息,按顺序分别为 goal、result 和f eedback,与 .srv 文件中的服务消息定义方式一样,使用“—”作为分隔符

这里我们直接修改功能包目录下的 package.xml 文件,添加如下内容

然后我们就需要进入到我们功能包目录下的 src 目录下,设计我们的代码文件,这里仅给出 c++ 代码:

点击查看完整代码:

TurtleMove_client.cpp
#include <actionlib/client/simple_action_client.h>
#include "learn_action/TurtleMoveAction.h"
#include <turtlesim/Pose.h> 
#include <turtlesim/Spawn.h>
#include <geometry_msgs/Twist.h>

typedef actionlib::SimpleActionClient<learn_action::TurtleMoveAction> Client;

struct Myturtle
{
    float x;
    float y;
    float theta;
}turtle_present_pose;

// 当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
        const learn_action::TurtleMoveResultConstPtr& result)
{
    ROS_INFO("Yay! The TurtleMove is finished!");
    ros::shutdown();
}

// 当action激活后会调用该回调函数一次
void activeCb()
{
    ROS_INFO("Goal just went active");
}

// 收到feedback后调用该回调函数
void feedbackCb(const learn_action::TurtleMoveFeedbackConstPtr& feedback)
{
    ROS_INFO(" present_pose : %f  %f  %f", feedback->present_turtle_x,
                   feedback->present_turtle_y,feedback->present_turtle_theta);
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "TurtleMove_client");

    // 定义一个客户端
    Client client("TurtleMove", true);

    // 等待服务器端
    ROS_INFO("Waiting for action server to start.");
    client.waitForServer();
    ROS_INFO("Action server started, sending goal.");

    // 创建一个action的goal
    learn_action::TurtleMoveGoal goal;
    goal.turtle_target_x = 1;
    goal.turtle_target_y = 1;
    goal.turtle_target_theta = 0;

    // 发送action的goal给服务器端,并且设置回调函数
    client.sendGoal(goal,  &doneCb, &activeCb, &feedbackCb);

    ros::spin();

    return 0;
}
TurtleMove_server.cpp
/*
   此程序通过通过动作编程实现由client发布一个目标位置
   然后控制Turtle运动到目标位置的过程
 */
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "learn_action/TurtleMoveAction.h"
#include <turtlesim/Pose.h> 
#include <turtlesim/Spawn.h>
#include <geometry_msgs/Twist.h>

typedef actionlib::SimpleActionServer<learn_action::TurtleMoveAction> Server;

struct Myturtle
{
    float x;
    float y;
    float theta;
}turtle_original_pose,turtle_target_pose;

ros::Publisher turtle_vel;

void posecallback(const turtlesim::PoseConstPtr& msg) 
{
  ROS_INFO("Turtle1_position:(%f,%f,%f)",msg->x,msg->y,msg->theta);
  turtle_original_pose.x=msg->x; 
  turtle_original_pose.y=msg->y;
  turtle_original_pose.theta=msg->theta;
 }

// 收到action的goal后调用该回调函数
void execute(const learn_action::TurtleMoveGoalConstPtr& goal, Server* as)
{
    learn_action::TurtleMoveFeedback feedback;

    ROS_INFO("TurtleMove is working.");
    turtle_target_pose.x=goal->turtle_target_x;
    turtle_target_pose.y=goal->turtle_target_y; 
    turtle_target_pose.theta=goal->turtle_target_theta;

    geometry_msgs::Twist vel_msgs;
    float break_flag;

    while(1)
    {
        ros::Rate r(10);

        vel_msgs.angular.z = 4.0 * (atan2(turtle_target_pose.y-turtle_original_pose.y,
                                   turtle_target_pose.x-turtle_original_pose.x)-turtle_original_pose.theta);
        vel_msgs.linear.x = 0.5 * sqrt(pow(turtle_target_pose.x-turtle_original_pose.x, 2) +
                                      pow(turtle_target_pose.y-turtle_original_pose.y, 2)); 
        break_flag=sqrt(pow(turtle_target_pose.x-turtle_original_pose.x, 2) +
                                        pow(turtle_target_pose.y-turtle_original_pose.y, 2));
        turtle_vel.publish(vel_msgs);

        feedback.present_turtle_x=turtle_original_pose.x;
        feedback.present_turtle_y=turtle_original_pose.y;
        feedback.present_turtle_theta=turtle_original_pose.theta;
        as->publishFeedback(feedback);
        ROS_INFO("break_flag=%f",break_flag);
        if(break_flag<0.1) break;
        r.sleep();
    }
        // 当action完成后,向客户端返回结果
        ROS_INFO("TurtleMove is finished.");
        as->setSucceeded();
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "TurtleMove_server");
    ros::NodeHandle n,turtle_node;
    //订阅小乌龟的位置信息
    ros::Subscriber sub = turtle_node.subscribe("turtle1/pose",10,&posecallback);
    //发布控制小乌龟运动的速度
    turtle_vel = turtle_node.advertise<geometry_msgs::Twist>("turtle1/cmd_vel",10);    
    // 定义一个服务器
    Server server(n, "TurtleMove", boost::bind(&execute, _1, &server), false);
    // 服务器开始运行
    server.start();
    ROS_INFO("server has started.");
    ros::spin();

    return 0;
}

这里我们的客户端调用回调函数进行接收消息,只有接收到客户端启用的消息,我们才能发送运动的目标终点,在 server 中同样使用 对应的回调函数,检测到 client 中发来的信息才能控制小海龟运行

下面就是我们最常见的修改 CMakeLists.txt 的部分:

修改如下部分:

添加如下内容:

add_executable(TurtleMove_client src/TurtleMove_client.cpp)
target_link_libraries(TurtleMove_client ${catkin_LIBRARIES})
add_dependencies(TurtleMove_client ${PROJECT_NAME}_gencpp)

add_executable(TurtleMove_server src/TurtleMove_server.cpp)
target_link_libraries(TurtleMove_server ${catkin_LIBRARIES})
add_dependencies(TurtleMove_server ${PROJECT_NAME}_gencpp)

编译运行

下面我们开始进行编译运行:

首先我们运行 ros-master 和小海龟节点,然后我们先运行 server 节点,这里由于我们并没有启动 client ,所以小海龟没有运动,而是循环输出当前的位置和方向:

然后启动 client ,这是我们的 server 端就已经成功的接收到了目标地址信息:

当命令行提示运动结束,我们就可以看到小海龟已经运动到了我们目标位置,我们的位置输出就已经没有变化了:

小海龟整体运行过程如下:

参考资料

ROS | 动作通信的编程实现

ROS动作编程之控制小乌龟运动到目标位置

おすすめ

転載: blog.csdn.net/m0_59161987/article/details/129610068