Note: This tutorial assumes that you have completed the previous tutorials: Communicating with a realtime joint controller. |
Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags. |
Adding a PID to a realtime joint controller
Description: このチュートリアルはあなたにリアルタイムJointコントローラーにPIDオブジェクトを加える方法を教えますTutorial Level: INTERMEDIATE
Next Tutorial: Capturing data from a controller
Introduction
このチュートリアルは我々が前のチュートリアルに作ったコントローラーをもとに作り上げます。ここで我々はコントローラーの中にPIDオブジェクトを使いスタートするでしょう。PIDコントローラーはcontrol_toolboxパッケージの一部です。
Adding the PID
The code
今我々は我々のコントローラーコードにpidコントローラーを加える必要があります。 結果のコードは下に示されます:
include/my_controller_pkg/my_controller_file.h
1 #include <pr2_controller_interface/controller.h>
2 #include <pr2_mechanism_model/joint.h>
3 #include <ros/ros.h>
4 #include <my_controller_pkg/SetAmplitude.h>
5 #include <control_toolbox/pid.h>
6
7
8 namespace my_controller_ns{
9
10 class MyControllerClass: public pr2_controller_interface::Controller
11 {
12 private:
13 bool setAmplitude(my_controller_pkg::SetAmplitude::Request& req,
14 my_controller_pkg::SetAmplitude::Response& resp);
15
16 pr2_mechanism_model::JointState* joint_state_;
17 double init_pos_;
18 double amplitude_;
19 ros::ServiceServer srv_;
20 control_toolbox::Pid pid_controller_;
21 pr2_mechanism_model::RobotState *robot_;
22 ros::Time time_of_last_cycle_;
23
24 public:
25 virtual bool init(pr2_mechanism_model::RobotState *robot,
26 ros::NodeHandle &n);
27 virtual void starting();
28 virtual void update();
29 virtual void stopping();
30 };
31 }
オリジナルのコードと比較して、ヘッダーは次の場所において変えました:
ここで我々はpidコントローラーと我々にサービスコールを使ってその場その場でコントローラーゲインに変わることを許すであろうゲインセッターを含みます。我々がその場その場でシヌソイドの振幅を変えるために加えたサービスに非常に類似しています。
ここで我々はストアするべきクラス変数を加えます
- pidオブジェクト、
- ロボットモデルへのポインタ(これは、init(..)メソッドで、我々に与えられるポインタです。そして
- 最後にコントローラーアップデートループが実行しました。今回、我々はアップデートループの2連続実行の間のデルタ時間を計算することができます。
次に、ここにcppファイルの新しいコードがあります: src/my_controller_file.cpp
1 #include "my_controller_pkg/my_controller_file.h"
2 #include <pluginlib/class_list_macros.h>
3
4 using namespace my_controller_ns;
5
6
7 /// Controller initialization in non-realtime
8 bool MyControllerClass::init(pr2_mechanism_model::RobotState *robot,
9 ros::NodeHandle &n)
10 {
11 // get joint name
12 std::string joint_name;
13 if (!n.getParam("joint_name", joint_name))
14 {
15 ROS_ERROR("No joint given in namespace: '%s')",
16 n.getNamespace().c_str());
17 return false;
18 }
19
20 // get pointer to joint state
21 joint_state_ = robot->getJointState(joint_name);
22 if (!joint_state_)
23 {
24 ROS_ERROR("MyController could not find joint named '%s'",
25 joint_name.c_str());
26 return false;
27 }
28
29 // advertise service to set amplitude
30 amplitude_ = 0.5;
31 srv_ = n.advertiseService("set_amplitude",
32 &MyControllerClass::setAmplitude, this);
33
34
35 // copy robot pointer so we can access time
36 robot_ = robot;
37
38 // construct pid controller
39 if (!pid_controller_.init(ros::NodeHandle(n, "pid_parameters"))){
40 ROS_ERROR("MyController could not construct PID controller for joint '%s'",
41 joint_name.c_str());
42 return false;
43 }
44
45 return true;
46 }
47
48
49 /// Controller startup in realtime
50 void MyControllerClass::starting()
51 {
52 init_pos_ = joint_state_->position_;
53 time_of_last_cycle_ = robot_->getTime();
54 pid_controller_.reset();
55 }
56
57
58 /// Controller update loop in realtime
59 void MyControllerClass::update()
60 {
61 double desired_pos = init_pos_ + amplitude_ * sin(ros::Time::now().toSec());
62 double current_pos = joint_state_->position_;
63
64 ros::Duration dt = robot_->getTime() - time_of_last_cycle_;
65 time_of_last_cycle_ = robot_->getTime();
66 joint_state_->commanded_effort_ = pid_controller_.updatePid(current_pos-desired_pos, dt);
67 }
68
69
70 /// Controller stopping in realtime
71 void MyControllerClass::stopping()
72 {}
73
74
75 /// Service call to set amplitude of sin
76 bool MyControllerClass::setAmplitude(my_controller_pkg::SetAmplitude::Request& req,
77 my_controller_pkg::SetAmplitude::Response& resp)
78 {
79 if (fabs(req.amplitude) < 2.0){
80 amplitude_ = req.amplitude;
81 ROS_INFO("Mycontroller: set amplitude too %f", req.amplitude);
82 }
83 else
84 ROS_WARN("MyController: requested amplitude %f too large", req.amplitude);
85
86 resp.amplitude = amplitude_;
87 return true;
88 }
89
90
91 /// Register controller to pluginlib
92 PLUGINLIB_REGISTER_CLASS(MyControllerPlugin,
93 my_controller_ns::MyControllerClass,
94 pr2_controller_interface::Controller)
cppファイルの変更は:
ここで我々はコピーをロボットにストアします。ロボットオブジェクトはコントローラーがトリガされる時間を得るために使われます。この時間はros::Time::now()!と異なります。ロボットモデルによって提供された時間はすべてのコントローラーのために同じであって、そしてコントローラーマネージャーがすべてのコントローラーをトリガしてスタートした時間です。
ここで我々は新しいpidコントローラーを初期化します。initメソッドはpidパラメータが指定されるnamespaceを指定するNodeHandleをとります。この場合、namespaceはコントローラー名プラス文字列"pid_parameters"です。このnamespaceは我々のlaunchファイルでpid値を設定するために重要です。
コントローラーのスタートのメソッドで我々はコントローラー時間をストアします、それで我々は後に2のアップデートループ間の時間における差を計算することができます。
60 {
61 double desired_pos = init_pos_ + amplitude_ * sin(ros::Time::now().toSec());
62 double current_pos = joint_state_->position_;
63
64 ros::Duration dt = robot_->getTime() - time_of_last_cycle_;
65 time_of_last_cycle_ = robot_->getTime();
66 joint_state_->commanded_effort_ = pid_controller_.updatePid(current_pos-desired_pos, dt);
67 }
これは我々が実際にpidコントローラーを使う場所です。pidはエラーシグナル、プラス時間間隔を必要とします。最初に我々は間隔"dt"、そして次に我々は実測と望ましい位置の間の差であるエラーシグナルを計算します。
pidコントローラーが負帰還を使うことに注意を払ってください (error = actual - desired)
Configuration
この新しいコードを編集するために、あなたは依存としてあなたのパッケージにcontrol_toolboxパッケージを加えます。あなたのmanifest.xmlに次のラインを加えてください:
<depend package="control_toolbox"/>
それで今我々はコントローラーを作る準備ができています。なぜなら我々はただ依存を加えました、そして我々はこの依存が同じくビルドを得ることを確認することを望みますから、我々はただ"make"の代わりに"rosmake"を使います
$ rosmake
次に、我々はpidに対してyamlファイルmy_controller.yamlにパラメータを加えます:
my_controller_name: type: MyControllerPlugin joint_name: r_shoulder_pan_joint pid_parameters: p: 10.0 i: 0.0 d: 0.0 i_clamp: 0.0
Running the controller
前と同じように、新しいコードはリアルタイムプロセスにロードされなければなりません。あなたは同様に、gazeboのすべてをリスタートしてください:
$ roslaunch gazebo_worlds empty_world.launch $ roslaunch pr2_gazebo pr2.launch
あるいはもしgazeboがまだ走っているなら、あなたはpr2_controller_managerをライブラリのリロードを求めるために使うことができます:
$ rosrun pr2_controller_manager pr2_controller_manager reload-libraries
そして今、我々の新しいコントローラーをスタートさせてください:
$ roslaunch my_controller.launch