plant_sim.cpp
Go to the documentation of this file.
00001 /***************************************************************************/ 
00037 // This file simulates a 1st or 2nd-order dynamic system, publishes its state,
00038 // and subscribes to a 'control_effort' topic. The control effort is used
00039 // to stabilize the servo.
00040 
00041 #include <ros/ros.h>
00042 #include <std_msgs/Float64.h>
00043 
00044 namespace plant_sim
00045 {
00046 // Global so it can be passed from the callback fxn to main
00047 static double control_effort = 0.0;
00048 static bool reverse_acting = false;
00049 }
00050 using namespace plant_sim;
00051 
00052 // Callback when something is published on 'control_effort'
00053 void controlEffortCallback(const std_msgs::Float64& control_effort_input)
00054 {
00055   // the stabilizing control effort
00056   if (reverse_acting)
00057   {
00058     control_effort = -control_effort_input.data;
00059   }
00060   else
00061   {
00062     control_effort = control_effort_input.data;
00063   }
00064 }
00065 
00066 int main(int argc, char** argv)
00067 {
00068   int plant_order = 1;
00069   double temp = 4.7;          // initial condition for first-order plant
00070   double displacement = 3.3;  // initial condition for second-order plant
00071 
00072   ros::init(argc, argv, "plant");
00073   ros::NodeHandle sim_node;
00074 
00075   while (ros::ok() && ros::Time(0) == ros::Time::now())
00076   {
00077     ROS_INFO("Plant_sim spinning waiting for time to become non-zero");
00078     sleep(1);
00079   }
00080 
00081   ros::NodeHandle node_priv("~");
00082   node_priv.param<int>("plant_order", plant_order, 1);
00083   node_priv.param<bool>("reverse_acting", reverse_acting, false);
00084 
00085   if (plant_order == 1)
00086   {
00087     ROS_INFO("Starting simulation of a first-order plant.");
00088   }
00089   else if (plant_order == 2)
00090   {
00091     ROS_INFO("Starting simulation of a second-order plant.");
00092   }
00093   else
00094   {
00095     ROS_ERROR("Error: Invalid plant type parameter, must be 1 or 2: %s", argv[1]);
00096     return -1;
00097   }
00098 
00099   // Advertise a plant state msg
00100   std_msgs::Float64 plant_state;
00101   ros::Publisher servo_state_pub = sim_node.advertise<std_msgs::Float64>("state", 1);
00102 
00103   // Subscribe to "control_effort" topic to get a controller_msg.msg
00104   ros::Subscriber sub = sim_node.subscribe("control_effort", 1, controlEffortCallback);
00105 
00106   int loop_counter = 0;
00107   double delta_t = 0.01;
00108   ros::Rate loop_rate(1 / delta_t);  // Control rate in Hz
00109 
00110   // Initialize 1st-order (e.g temp controller) process variables
00111   double temp_rate = 0;  // rate of temp change
00112 
00113   // Initialize 2nd-order (e.g. servo-motor with load) process variables
00114   double speed = 0;         // meters/sec
00115   double acceleration = 0;  // meters/sec^2
00116   double mass = 0.1;        // in kg
00117   double friction = 1.0;    // a decelerating force factor
00118   double stiction = 1;      // control_effort must exceed this before stationary servo moves
00119   double Kv = 1;            // motor constant: force (newtons) / volt
00120   double Kbackemf = 0;      // Volts of back-emf per meter/sec of speed
00121   double decel_force;       // decelerating force
00122 
00123   while (ros::ok())
00124   {
00125     ros::spinOnce();
00126 
00127     switch (plant_order)
00128     {
00129       case 1:  // First order plant
00130         temp_rate = (0.1 * temp) + control_effort;
00131         temp = temp + temp_rate * delta_t;
00132 
00133         plant_state.data = temp;
00134         break;
00135 
00136       case 2:  // Second order plant
00137         if (fabs(speed) < 0.001)
00138         {
00139           // if nearly stopped, stop it & require overcoming stiction to restart
00140           speed = 0;
00141           if (fabs(control_effort) < stiction)
00142           {
00143             control_effort = 0;
00144           }
00145         }
00146 
00147         // Update the servo.
00148         // control_effort: the voltage applied to the servo. Output from PID
00149         // controller. It is
00150         //   opposed by back emf (expressed as speed) to produce a net force.
00151         //   Range: -1 to +1
00152         // displacement: the actual value of the servo output position. Input to
00153         // PID controller
00154 
00155         decel_force = -(speed * friction);  // can be +ve or -ve. Linear with speed
00156         acceleration = ((Kv * (control_effort - (Kbackemf * speed)) + decel_force) / mass);  // a = F/m
00157         speed = speed + (acceleration * delta_t);
00158         displacement = displacement + speed * delta_t;
00159 
00160         plant_state.data = displacement;
00161         break;
00162 
00163       default:
00164         ROS_ERROR("Invalid plant_order: %d", plant_order);
00165         return (-1);
00166     }
00167 
00168     servo_state_pub.publish(plant_state);
00169     loop_rate.sleep();
00170   }
00171 
00172   return 0;
00173 }


pid
Author(s): Andy Zelenak , Paul Bouchier
autogenerated on Mon Apr 15 2019 02:40:47