Skip to content

Commit b43c60f

Browse files
authored
Merge pull request #72 from ye-luo-xi-tui/dev
Fix realtime loop
2 parents ad20c2d + b1c28cd commit b43c60f

File tree

9 files changed

+84
-55
lines changed

9 files changed

+84
-55
lines changed

rm_hw/include/rm_hw/control_loop.h

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,16 @@
3939

4040
#include "rm_hw/hardware_interface/hardware_interface.h"
4141

42-
// Timer
4342
#include <chrono>
44-
#include <utility>
43+
#include <thread>
4544

46-
// ROS
4745
#include <ros/ros.h>
48-
49-
// ROS control
5046
#include <controller_manager/controller_manager.h>
5147

5248
namespace rm_hw
5349
{
5450
using namespace std::chrono;
51+
using clock = high_resolution_clock;
5552

5653
class RmRobotHWLoop
5754
{
@@ -63,6 +60,8 @@ class RmRobotHWLoop
6360
* @param hardware_interface A pointer which point to hardware_interface.
6461
*/
6562
RmRobotHWLoop(ros::NodeHandle& nh, std::shared_ptr<RmRobotHW> hardware_interface);
63+
64+
~RmRobotHWLoop();
6665
/** \brief Timed method that reads current hardware's state, runs the controller code once and sends the new commands
6766
* to the hardware.
6867
*
@@ -73,22 +72,21 @@ class RmRobotHWLoop
7372
* linearly increasing.
7473
*/
7574

76-
void update(const ros::TimerEvent&);
75+
void update();
7776

7877
private:
7978
// Startup and shutdown of the internal node inside a roscpp program
8079
ros::NodeHandle nh_;
8180

8281
// Settings
83-
ros::Duration desired_update_freq_;
8482
double cycle_time_error_threshold_{};
8583

8684
// Timing
87-
ros::Timer loop_timer_;
88-
ros::Duration elapsed_time_;
85+
std::thread loop_thread_;
86+
std::atomic_bool loop_running_;
8987
double loop_hz_{};
90-
steady_clock::time_point last_time_;
91-
steady_clock::time_point current_time_;
88+
ros::Duration elapsed_time_;
89+
clock::time_point last_time_;
9290

9391
/** ROS Controller Manager and Runner
9492

rm_hw/include/rm_hw/hardware_interface/can_bus.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class CanBus
6161
* \param bus_name Bus's name(example: can0).
6262
* \param data_ptr Pointer which point to CAN data.
6363
*/
64-
CanBus(const std::string& bus_name, CanDataPtr data_ptr);
64+
CanBus(const std::string& bus_name, CanDataPtr data_ptr, int thread_priority);
6565
/** \brief Read active data from read_buffer_ to data_ptr_, such as position, velocity, torque and so on. Clear
6666
* read_buffer_ after reading.
6767
*

rm_hw/include/rm_hw/hardware_interface/hardware_interface.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class RmRobotHW : public hardware_interface::RobotHW
102102
*/
103103
void write(const ros::Time& time, const ros::Duration& period) override;
104104

105+
void setCanBusThreadPriority(int thread_priority);
106+
105107
private:
106108
/** \brief Check whether some coefficients that are related to actuator are set up and load these coefficients.
107109
*
@@ -177,6 +179,7 @@ class RmRobotHW : public hardware_interface::RobotHW
177179
ros::ServiceServer service_server_;
178180

179181
bool is_actuator_specified_ = false;
182+
int thread_priority_;
180183
// Interface
181184
std::vector<CanBus*> can_buses_{};
182185
GpioManager gpio_manager_{};

rm_hw/include/rm_hw/hardware_interface/socketcan.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class SocketCAN
7373
*
7474
* \returns \c true if it successfully open and bind socket.
7575
*/
76-
bool open(const std::string& interface, boost::function<void(const can_frame& frame)> handler);
76+
bool open(const std::string& interface, boost::function<void(const can_frame& frame)> handler, int thread_priority);
7777
/** \brief Close and unbind socket.
7878
*
7979
*/
@@ -91,7 +91,7 @@ class SocketCAN
9191
/** \brief Starts a new thread, that will wait for socket events.
9292
*
9393
*/
94-
bool startReceiverThread();
94+
bool startReceiverThread(int thread_priority);
9595
/**
9696
* Pointer to a function which shall be called
9797
* when frames are being received from the CAN bus

rm_hw/src/control_loop.cpp

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,51 +36,68 @@
3636
//
3737
#include "rm_hw/control_loop.h"
3838

39-
rm_hw::RmRobotHWLoop::RmRobotHWLoop(ros::NodeHandle& nh, std::shared_ptr<RmRobotHW> hardware_interface)
39+
namespace rm_hw
40+
{
41+
RmRobotHWLoop::RmRobotHWLoop(ros::NodeHandle& nh, std::shared_ptr<RmRobotHW> hardware_interface)
4042
: nh_(nh), hardware_interface_(std::move(hardware_interface))
4143
{
42-
// Create the controller manager
43-
controller_manager_.reset(new controller_manager::ControllerManager(hardware_interface_.get(), nh_));
44-
45-
// Load rosparams
46-
int error = 0;
44+
// Load ros params
45+
int error = 0, thread_priority;
4746
ros::NodeHandle nh_p("~");
4847
error += !nh_p.getParam("loop_frequency", loop_hz_);
4948
error += !nh_p.getParam("cycle_time_error_threshold", cycle_time_error_threshold_);
49+
error += !nh_p.getParam("thread_priority", thread_priority);
5050
if (error > 0)
5151
{
52-
char error_message[] =
53-
"could not retrieve one of the required parameters\n\trm_hw/loop_hz or rm_hw/cycle_time_error_threshold";
52+
char error_message[] = "could not retrieve one of the required parameters\n\trm_hw/loop_hz or "
53+
"rm_hw/cycle_time_error_threshold or "
54+
"rm_hw/thread_priority";
5455
ROS_ERROR_STREAM(error_message);
5556
throw std::runtime_error(error_message);
5657
}
5758

59+
// Initialise the hardware interface:
60+
// 1. retrieve configuration from rosparam
61+
// 2. initialise the hardware and interface it with ros_control
62+
hardware_interface_->setCanBusThreadPriority(thread_priority);
63+
hardware_interface_->init(nh, nh_p);
64+
// Create the controller manager
65+
controller_manager_.reset(new controller_manager::ControllerManager(hardware_interface_.get(), nh_));
66+
5867
// Get current time for use with first update
59-
last_time_ = steady_clock::now();
68+
last_time_ = clock::now();
6069

61-
// Start timer that will periodically call RmRobotHWLoop::update
62-
desired_update_freq_ = ros::Duration(1 / loop_hz_);
63-
loop_timer_ = nh_.createTimer(desired_update_freq_, &RmRobotHWLoop::update, this);
70+
// Setup loop thread
71+
loop_thread_ = std::thread([&]() {
72+
while (loop_running_)
73+
{
74+
if (loop_running_)
75+
update();
76+
}
77+
});
78+
sched_param sched{ .sched_priority = thread_priority };
79+
if (pthread_setschedparam(loop_thread_.native_handle(), SCHED_FIFO, &sched) != 0)
80+
ROS_WARN("Failed to set threads priority (one possible reason could be that the user and the group permissions "
81+
"are not set properly.).\n");
6482
}
6583

66-
void rm_hw::RmRobotHWLoop::update(const ros::TimerEvent&)
84+
void RmRobotHWLoop::update()
6785
{
86+
const auto current_time = clock::now();
87+
// Compute desired duration rounded to clock decimation
88+
const duration<double> desired_duration(1.0 / loop_hz_);
6889
// Get change in time
69-
current_time_ = steady_clock::now();
70-
duration<double> time_span = duration_cast<duration<double>>(current_time_ - last_time_);
90+
duration<double> time_span = duration_cast<duration<double>>(current_time - last_time_);
7191
elapsed_time_ = ros::Duration(time_span.count());
72-
last_time_ = current_time_;
92+
last_time_ = current_time;
7393

7494
// Check cycle time for excess delay
75-
const double cycle_time_error = (elapsed_time_ - desired_update_freq_).toSec();
95+
const double cycle_time_error = (elapsed_time_ - ros::Duration(desired_duration.count())).toSec();
7696
if (cycle_time_error > cycle_time_error_threshold_)
77-
{
7897
ROS_WARN_STREAM("Cycle time exceeded error threshold by: " << cycle_time_error - cycle_time_error_threshold_
7998
<< "s, "
8099
<< "cycle time: " << elapsed_time_ << "s, "
81100
<< "threshold: " << cycle_time_error_threshold_ << "s");
82-
}
83-
84101
// Input
85102
// get the hardware's state
86103
hardware_interface_->read(ros::Time::now(), elapsed_time_);
@@ -92,4 +109,16 @@ void rm_hw::RmRobotHWLoop::update(const ros::TimerEvent&)
92109
// Output
93110
// send the new command to hardware
94111
hardware_interface_->write(ros::Time::now(), elapsed_time_);
112+
113+
// Sleep
114+
const auto sleep_till = current_time + duration_cast<clock::duration>(desired_duration);
115+
std::this_thread::sleep_until(sleep_till);
116+
}
117+
118+
RmRobotHWLoop::~RmRobotHWLoop()
119+
{
120+
loop_running_ = false;
121+
if (loop_thread_.joinable())
122+
loop_thread_.join();
95123
}
124+
} // namespace rm_hw

rm_hw/src/hardware_interface/can_bus.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@
4141
#include <rm_common/math_utilities.h>
4242
namespace rm_hw
4343
{
44-
CanBus::CanBus(const std::string& bus_name, CanDataPtr data_ptr) : bus_name_(bus_name), data_ptr_(data_ptr)
44+
CanBus::CanBus(const std::string& bus_name, CanDataPtr data_ptr, int thread_priority)
45+
: bus_name_(bus_name), data_ptr_(data_ptr)
4546
{
4647
// Initialize device at can_device, false for no loop back.
47-
while (!socket_can_.open(bus_name, boost::bind(&CanBus::frameCallback, this, _1)) && ros::ok())
48+
while (!socket_can_.open(bus_name, boost::bind(&CanBus::frameCallback, this, _1), thread_priority) && ros::ok())
4849
ros::Duration(.5).sleep();
4950

5051
ROS_INFO("Successfully connected to %s.", bus_name.c_str());

rm_hw/src/hardware_interface/hardware_interface.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@ bool RmRobotHW::init(ros::NodeHandle& root_nh, ros::NodeHandle& robot_hw_nh)
7777
{
7878
std::string bus_name = xml_rpc_value[i];
7979
if (bus_name.find("can") != std::string::npos)
80-
can_buses_.push_back(new CanBus(bus_name, CanDataPtr{ .type2act_coeffs_ = &type2act_coeffs_,
81-
.id2act_data_ = &bus_id2act_data_[bus_name],
82-
.id2imu_data_ = &bus_id2imu_data_[bus_name],
83-
.id2tof_data_ = &bus_id2tof_data_[bus_name] }));
80+
can_buses_.push_back(new CanBus(bus_name,
81+
CanDataPtr{ .type2act_coeffs_ = &type2act_coeffs_,
82+
.id2act_data_ = &bus_id2act_data_[bus_name],
83+
.id2imu_data_ = &bus_id2imu_data_[bus_name],
84+
.id2tof_data_ = &bus_id2tof_data_[bus_name] },
85+
thread_priority_));
8486
else
8587
ROS_ERROR_STREAM("Unknown bus: " << bus_name);
8688
}
@@ -175,6 +177,11 @@ void RmRobotHW::write(const ros::Time& time, const ros::Duration& period)
175177
publishActuatorState(time);
176178
}
177179

180+
void RmRobotHW::setCanBusThreadPriority(int thread_priority)
181+
{
182+
thread_priority_ = thread_priority;
183+
}
184+
178185
void RmRobotHW::publishActuatorState(const ros::Time& time)
179186
{
180187
if (!is_actuator_specified_)

rm_hw/src/hardware_interface/socketcan.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ SocketCAN::~SocketCAN()
5454
this->close();
5555
}
5656

57-
bool SocketCAN::open(const std::string& interface, boost::function<void(const can_frame& frame)> handler)
57+
bool SocketCAN::open(const std::string& interface, boost::function<void(const can_frame& frame)> handler,
58+
int thread_priority)
5859
{
5960
reception_handler = std::move(handler);
6061
// Request a socket
@@ -86,7 +87,7 @@ bool SocketCAN::open(const std::string& interface, boost::function<void(const ca
8687
return false;
8788
}
8889
// Start a separate, event-driven thread for frame reception
89-
return startReceiverThread();
90+
return startReceiverThread(thread_priority);
9091
}
9192

9293
void SocketCAN::close()
@@ -158,7 +159,7 @@ static void* socketcan_receiver_thread(void* argv)
158159
return nullptr;
159160
}
160161

161-
bool SocketCAN::startReceiverThread()
162+
bool SocketCAN::startReceiverThread(int thread_priority)
162163
{
163164
// Frame reception is accomplished in a separate, event-driven thread.
164165
// See also: https://www.thegeekstuff.com/2012/04/create-threads-in-linux/
@@ -170,6 +171,8 @@ bool SocketCAN::startReceiverThread()
170171
return false;
171172
}
172173
ROS_INFO("Successfully started receiver thread with ID %lu", receiver_thread_id_);
174+
sched_param sched{ .sched_priority = thread_priority };
175+
pthread_setschedparam(receiver_thread_id_, SCHED_FIFO, &sched);
173176
return true;
174177
}
175178

rm_hw/src/rm_hw.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ int main(int argc, char** argv)
4141
{
4242
ros::init(argc, argv, "rm_hw");
4343
ros::NodeHandle nh;
44-
ros::NodeHandle robot_hw_nh("~");
4544

4645
// Run the hardware interface node
4746
// -------------------------------
@@ -52,21 +51,10 @@ int main(int argc, char** argv)
5251
ros::AsyncSpinner spinner(2);
5352
spinner.start();
5453

55-
struct sched_param params
56-
{
57-
.sched_priority = 95
58-
};
59-
if (sched_setscheduler(0, SCHED_FIFO, &params) == -1)
60-
ROS_ERROR("Set scheduler failed, RUN THIS NODE AS SUPER USER.\n");
61-
6254
try
6355
{
6456
// Create the hardware interface specific to your robot
6557
std::shared_ptr<rm_hw::RmRobotHW> rm_hw_hw_interface = std::make_shared<rm_hw::RmRobotHW>();
66-
// Initialise the hardware interface:
67-
// 1. retrieve configuration from rosparam
68-
// 2. initialize the hardware and interface it with ros_control
69-
rm_hw_hw_interface->init(nh, robot_hw_nh);
7058

7159
// Start the control loop
7260
rm_hw::RmRobotHWLoop control_loop(nh, rm_hw_hw_interface);

0 commit comments

Comments
 (0)