Skip to content

Commit 461af7f

Browse files
author
Florent Lamiraux
committed
[RSSotController] Better handle subsampling.
Formerly, when the computation of the graph was subsampled, computation was launched once in several iterations. As a result, the iteration when the computation was launched was usually exceeding Roscontrol time period and the scheduler would catch up at the next iteration since no computation was launched. With this commit, the computation of the control graph is launched in a separate thread in such a way that reatime constraints are satisfied.
1 parent 1017637 commit 461af7f

File tree

2 files changed

+117
-28
lines changed

2 files changed

+117
-28
lines changed

src/roscontrol-sot-controller.cpp

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,15 @@ RCSotController::RCSotController()
7373
: // Store 32 DoFs for 5 minutes (1 Khz: 5*60*1000)
7474
// -> 124 Mo of data.
7575
type_name_("RCSotController"), simulation_mode_(false),
76-
accumulated_time_(0.0), jitter_(0.0), verbosity_level_(0) {
76+
subSampling_(0), step_(0), verbosity_level_(0), io_service_(),
77+
io_work_(io_service_), sotComputing_(false)
78+
{
7779
RESETDEBUG4();
7880
}
7981

8082
RCSotController::~RCSotController() {
83+
io_service_.stop();
84+
thread_pool_.join_all();
8185
std::string afilename("/tmp/sot.log");
8286

8387
RcSotLog_.record(DataOneIter_);
@@ -620,13 +624,6 @@ bool RCSotController::readParamsControlMode(ros::NodeHandle &robot_nh) {
620624
}
621625

622626
bool RCSotController::readParamsdt(ros::NodeHandle &robot_nh) {
623-
/// Reading the jitter is optional but it is a very good idea.
624-
if (robot_nh.hasParam("/sot_controller/jitter")) {
625-
robot_nh.getParam("/sot_controller/jitter", jitter_);
626-
if (verbosity_level_ > 0)
627-
ROS_INFO_STREAM("jitter: " << jitter_);
628-
}
629-
630627
/// Read /sot_controller/dt to know what is the control period
631628
if (robot_nh.hasParam("/sot_controller/dt")) {
632629
robot_nh.getParam("/sot_controller/dt", dt_);
@@ -952,13 +949,19 @@ void RCSotController::readControl(
952949
DataOneIter_.controls[i] = command_[i];
953950
}
954951

955-
} else
952+
} else{
956953
ROS_INFO_STREAM("no control.");
954+
localStandbyEffortControlMode(ros::Duration(dtRos_));
955+
localStandbyVelocityControlMode(ros::Duration(dtRos_));
956+
localStandbyPositionControlMode();
957+
}
957958
}
958959

959960
void RCSotController::one_iteration() {
961+
sotComputing_ = true;
960962
// Chrono start
961963
RcSotLog_.start_it();
964+
962965
/// Update the sensors.
963966
fillSensors();
964967

@@ -973,21 +976,24 @@ void RCSotController::one_iteration() {
973976

974977
throw e;
975978
}
976-
977979
try {
978-
sotController_->getControl(controlValues_);
980+
sotController_->getControl(controlValues_copy_);
979981
} catch (std::exception &e) {
980982
std::cerr << "Failure happened during one_iteration(): "
981983
<< "when calling getControl " << std::endl;
982984
std::cerr << __FILE__ << " " << __LINE__ << std::endl
983-
<< e.what() << std::endl;;
985+
<< e.what() << std::endl;
984986
throw e;
985987
}
986988

987-
988-
/// Read the control values
989-
readControl(controlValues_);
990-
989+
// Wait until last subsampling step to write result in controlValues_
990+
while(step_ != subSampling_ - 1){
991+
ros::Duration(.01*dtRos_).sleep();
992+
}
993+
// mutex
994+
mutex_.lock();
995+
controlValues_ = controlValues_copy_;
996+
mutex_.unlock();
991997
// Chrono stop.
992998
double it_duration = RcSotLog_.stop_it();
993999
if (it_duration > dt_) {
@@ -997,6 +1003,7 @@ void RCSotController::one_iteration() {
9971003

9981004
/// Store everything in Log.
9991005
RcSotLog_.record(DataOneIter_);
1006+
sotComputing_ = false;
10001007
}
10011008

10021009
void RCSotController::localStandbyEffortControlMode(
@@ -1085,16 +1092,81 @@ void RCSotController::localStandbyPositionControlMode() {
10851092
first_time = false;
10861093
}
10871094

1095+
void RCSotController::computeSubSampling(const ros::Duration &period)
1096+
{
1097+
if (subSampling_ != 0) return; // already computed
1098+
dtRos_ = period.toSec();
1099+
double ratio = dt_/dtRos_;
1100+
if (fabs(ratio - std::round(ratio)) > 1e-8){
1101+
std::ostringstream os;
1102+
os << "SoT period (" << dt_ << ") is not a multiple of roscontrol period ("
1103+
<< dtRos_ << "). Modify rosparam /sot_controller/dt to a "
1104+
"multiple of roscontrol period.";
1105+
throw std::runtime_error(os.str().c_str());
1106+
}
1107+
subSampling_ = static_cast<std::size_t>(std::round(ratio));
1108+
// First time method update is called, step_ will turn to 1.
1109+
step_ = subSampling_ -1;
1110+
ROS_INFO_STREAM("Subsampling SoT graph computation by ratio "
1111+
<< subSampling_);
1112+
if (subSampling_ != 1){
1113+
// create thread in this method instead of in constructor to inherit the
1114+
// same priority level.
1115+
// If subsampling is equal to one, the control graph is evaluated in the
1116+
// same thread.
1117+
thread_pool_.create_thread(boost::bind(&boost::asio::io_service::run,
1118+
&io_service_));
1119+
}
1120+
1121+
}
10881122
void RCSotController::update(const ros::Time &, const ros::Duration &period) {
10891123
// Do not send any control if the dynamic graph is not started
10901124
if (!isDynamicGraphStopped()) {
1125+
// Increment step at beginning of this method since the end time may
1126+
// vary a lot.
1127+
++ step_;
1128+
if (step_ == subSampling_) step_ = 0;
10911129
try {
1092-
double periodInSec = period.toSec();
1093-
if (periodInSec + accumulated_time_ > dt_ - jitter_) {
1094-
one_iteration();
1095-
accumulated_time_ = 0.0;
1096-
} else
1097-
accumulated_time_ += periodInSec;
1130+
// If subsampling is equal to 1, i.e. no subsampling, evaluate
1131+
// control graph in this thread
1132+
if (subSampling_ == 1){
1133+
one_iteration();
1134+
} else {
1135+
if (step_ == 0){
1136+
// If previous graph evaluation is not finished, do not start a new
1137+
// one.
1138+
if (!sotComputing_){
1139+
io_service_.post(boost::bind(&RCSotController::one_iteration,this));
1140+
} else{
1141+
ROS_INFO_STREAM("Sot computation not finished yet.");
1142+
}
1143+
// protected read control
1144+
mutex_.lock();
1145+
/// Read the control values
1146+
readControl(controlValues_);
1147+
mutex_.unlock();
1148+
} else{
1149+
// protected read control
1150+
mutex_.lock();
1151+
// If the robot is controlled in position, update the reference
1152+
// posture with the latest velocity.
1153+
// Note that the entry "velocity" only exists if the robot is
1154+
// controlled in position.
1155+
if (controlValues_.find("velocity") != controlValues_.end()){
1156+
std::vector<double> control = controlValues_["control"].getValues();
1157+
const std::vector<double>& velocity = controlValues_["velocity"].
1158+
getValues();
1159+
for (std::size_t i=0; i<control.size(); ++i){
1160+
control[i] += dtRos_ * velocity[i];
1161+
}
1162+
controlValues_["control"].setValues(control);
1163+
}
1164+
/// Read the control values
1165+
readControl(controlValues_);
1166+
mutex_.unlock();
1167+
}
1168+
}
1169+
10981170
} catch (std::exception const &exc) {
10991171
ROS_ERROR_STREAM("Failure happened during one_iteration evaluation: "
11001172
<< exc.what() << "\nUse gdb to investiguate the problem\n"
@@ -1107,6 +1179,7 @@ void RCSotController::update(const ros::Time &, const ros::Duration &period) {
11071179
throw;
11081180
}
11091181
} else {
1182+
computeSubSampling(period);
11101183
/// Update the sensors.
11111184
fillSensors();
11121185
try {

src/roscontrol-sot-controller.hh

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
#include <map>
99
#include <string>
10+
#include <atomic>
11+
12+
#include <boost/thread/thread.hpp>
13+
#include <boost/asio/io_service.hpp>
1014

1115
#pragma GCC diagnostic push
1216
#pragma GCC system_header
@@ -74,6 +78,8 @@ protected:
7478
rc_sot_system::DataToLog DataOneIter_;
7579

7680
private:
81+
82+
void computeSubSampling(const ros::Duration &period);
7783
/// @{ \name Ros-control related fields
7884

7985
/// \brief Vector of joint handles.
@@ -140,12 +146,12 @@ private:
140146
/// * torques
141147
std::map<std::string, std::string> mapFromRCToSotDevice_;
142148

143-
/// To be able to subsample control period.
144-
double accumulated_time_;
145-
146-
/// Jitter for the subsampling.
147-
double jitter_;
148-
149+
/// ratio between sot control period and roscontrol control period
150+
std::size_t subSampling_;
151+
/// iteration index of the subsampling. Ranges from 0 to subSampling_-1
152+
std::atomic <std::size_t> step_;
153+
/// double roscontrol sampling period
154+
double dtRos_;
149155
/// \brief Verbosity level for ROS messages during initRequest/initialization
150156
/// phase. 0: no messages or error 1: info 2: debug
151157
int verbosity_level_;
@@ -156,6 +162,15 @@ private:
156162
/// Profile log
157163
rc_sot_system::ProfileLog profileLog_;
158164

165+
/// thread pool
166+
boost::asio::io_service io_service_;
167+
boost::asio::io_service::work io_work_;
168+
boost::thread_group thread_pool_;
169+
// mutex to protect access to controlValues_
170+
std::mutex mutex_;
171+
// Flag informing whether the graph computation is running to avoid starting
172+
// a computation if the previous one has no finished.
173+
bool sotComputing_;
159174
public:
160175
RCSotController();
161176

@@ -273,6 +288,7 @@ protected:
273288

274289
/// Map of control values
275290
std::map<std::string, dgs::ControlValues> controlValues_;
291+
std::map<std::string, dgs::ControlValues> controlValues_copy_;
276292

277293
/// Control period
278294
double dt_;

0 commit comments

Comments
 (0)