Skip to content

Commit fec992b

Browse files
Add additional Puma diagnostics (#270)
1 parent ac56213 commit fec992b

File tree

6 files changed

+124
-1
lines changed

6 files changed

+124
-1
lines changed

clearpath_motor_drivers/puma_motor_driver/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ find_package(ament_cmake REQUIRED)
1010
find_package(can_msgs REQUIRED)
1111
find_package(clearpath_motor_msgs REQUIRED)
1212
find_package(clearpath_ros2_socketcan_interface REQUIRED)
13+
find_package(diagnostic_updater REQUIRED)
1314
find_package(rclcpp REQUIRED)
1415
find_package(std_msgs REQUIRED)
1516
find_package(sensor_msgs REQUIRED)
@@ -20,6 +21,7 @@ set(DEPENDENCIES
2021
can_msgs
2122
clearpath_motor_msgs
2223
clearpath_ros2_socketcan_interface
24+
diagnostic_updater
2325
rclcpp
2426
std_msgs
2527
sensor_msgs

clearpath_motor_drivers/puma_motor_driver/include/puma_motor_driver/driver.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
3232

3333
#include "clearpath_motor_msgs/msg/puma_status.hpp"
3434

35+
#include "diagnostic_updater/update_functions.hpp"
36+
3537
#include "puma_motor_driver/can_proto.hpp"
3638

3739
namespace puma_motor_driver
@@ -460,6 +462,10 @@ class Driver
460462
}
461463
};
462464

465+
// Diagnostics
466+
void runFreqStatus(diagnostic_updater::DiagnosticStatusWrapper & stat);
467+
void driverUpdateDiagnostics(diagnostic_updater::DiagnosticStatusWrapper & stat, bool updating);
468+
463469
private:
464470
std::shared_ptr<clearpath_ros2_socketcan_interface::SocketCANInterface> interface_;
465471
std::shared_ptr<rclcpp::Node> nh_;
@@ -521,6 +527,10 @@ class Driver
521527
Field * ictrlFieldForMessage(uint32_t api);
522528
Field * statusFieldForMessage(uint32_t api);
523529
Field * cfgFieldForMessage(uint32_t api);
530+
531+
// Frequency Status for diagnostics
532+
std::shared_ptr<double> can_feedback_rate_; // Shared ptr prevents copy errors of FrequencyStatus
533+
std::shared_ptr<diagnostic_updater::FrequencyStatus> can_feedback_freq_status_;
524534
};
525535

526536
} // namespace puma_motor_driver

clearpath_motor_drivers/puma_motor_driver/include/puma_motor_driver/multi_puma_node.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
2424
#ifndef PUMA_MOTOR_DRIVER_MULTI_PUMA_NODE_H
2525
#define PUMA_MOTOR_DRIVER_MULTI_PUMA_NODE_H
2626

27+
#include <map>
28+
2729
#include "rclcpp/rclcpp.hpp"
2830
#include "std_msgs/msg/float64.hpp"
2931
#include "sensor_msgs/msg/joint_state.hpp"
@@ -35,6 +37,8 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
3537

3638
#include "clearpath_ros2_socketcan_interface/socketcan_interface.hpp"
3739

40+
#include "diagnostic_updater/diagnostic_updater.hpp"
41+
3842
#include "puma_motor_driver/driver.hpp"
3943
// #include "puma_motor_driver/diagnostic_updater.hpp"
4044

@@ -121,6 +125,9 @@ class MultiPumaNode
121125
void run();
122126

123127
private:
128+
using DiagnosticStatusWrapper = diagnostic_updater::DiagnosticStatusWrapper;
129+
using PumaStatus = clearpath_motor_msgs::msg::PumaStatus;
130+
124131
std::shared_ptr<clearpath_ros2_socketcan_interface::SocketCANInterface> interface_;
125132
std::vector<puma_motor_driver::Driver> drivers_;
126133

@@ -149,6 +156,34 @@ class MultiPumaNode
149156
rclcpp::Subscription<sensor_msgs::msg::JointState>::SharedPtr cmd_sub_;
150157
rclcpp::TimerBase::SharedPtr run_timer_;
151158

159+
// Diagnostic Updater
160+
diagnostic_updater::Updater updater_;
161+
162+
// Diagnostic labels
163+
const std::map<uint8_t, std::string> MODE_FLAG_LABELS_ = {
164+
{PumaStatus::MODE_VOLTAGE, "Voltage"},
165+
{PumaStatus::MODE_CURRENT, "Current"},
166+
{PumaStatus::MODE_SPEED, "Speed"},
167+
{PumaStatus::MODE_POSITION, "Position"},
168+
{PumaStatus::MODE_VCOMP, "V-Comp"},
169+
};
170+
const std::map<uint8_t, std::string> FAULT_FLAG_LABELS_ = {
171+
{PumaStatus::FAULT_CURRENT, "Current Fault"},
172+
{PumaStatus::FAULT_TEMPERATURE, "Temperature Fault"},
173+
{PumaStatus::FAULT_BUS_VOLTAGE, "Bus Voltage Fault"},
174+
{PumaStatus::FAULT_BRIDGE_DRIVER, "Bridge Driver Fault"},
175+
};
176+
const std::map<std::string, std::string> PUMA_MOTOR_LABELS_ = {
177+
{"front_left_wheel_joint", "Front Left"},
178+
{"front_right_wheel_joint", "Front Right"},
179+
{"rear_left_wheel_joint", "Rear Left"},
180+
{"rear_right_wheel_joint", "Rear Right"},
181+
};
182+
183+
184+
// Diagnostic Tasks
185+
void driverDiagnostic(DiagnosticStatusWrapper & stat, int i);
186+
void protectionDiagnostic(DiagnosticStatusWrapper & stat);
152187
};
153188

154189
#endif // PUMA_MOTOR_DRIVER_PUMA_NODE_H

clearpath_motor_drivers/puma_motor_driver/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<depend>can_msgs</depend>
1414
<depend version_gte="2.4.0">clearpath_motor_msgs</depend>
1515
<depend version_gte="2.1.1">clearpath_ros2_socketcan_interface</depend>
16+
<depend>diagnostic_updater</depend>
1617
<depend>rclcpp</depend>
1718
<depend>sensor_msgs</depend>
1819
<depend>std_msgs</depend>

clearpath_motor_drivers/puma_motor_driver/src/driver.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
3131
#include <math.h>
3232
#include "rclcpp/rclcpp.hpp"
3333

34+
// must match firmware
35+
#define CAN_FEEDBACK_RATE 40.0
36+
3437
namespace puma_motor_driver
3538
{
3639

@@ -74,6 +77,15 @@ Driver::Driver(
7477
encoder_cpr_(1),
7578
gear_ratio_(1)
7679
{
80+
can_feedback_rate_ = std::make_shared<double>(CAN_FEEDBACK_RATE);
81+
can_feedback_freq_status_ = std::make_shared<diagnostic_updater::FrequencyStatus>(
82+
diagnostic_updater::FrequencyStatusParam(
83+
can_feedback_rate_.get(),
84+
can_feedback_rate_.get(),
85+
0.1,
86+
5
87+
)
88+
);
7789
}
7890

7991
void Driver::processMessage(const can_msgs::msg::Frame::SharedPtr received_msg)
@@ -96,14 +108,19 @@ void Driver::processMessage(const can_msgs::msg::Frame::SharedPtr received_msg)
96108
field = statusFieldForMessage(received_api);
97109
} else if ((received_api & CAN_MSGID_API_M & CAN_API_MC_ICTRL) == CAN_API_MC_ICTRL) {
98110
field = ictrlFieldForMessage(received_api);
111+
can_feedback_freq_status_->tick();
99112
} else if ((received_api & CAN_MSGID_API_M & CAN_API_MC_POS) == CAN_API_MC_POS) {
100113
field = posFieldForMessage(received_api);
114+
can_feedback_freq_status_->tick();
101115
} else if ((received_api & CAN_MSGID_API_M & CAN_API_MC_VCOMP) == CAN_API_MC_VCOMP) {
102116
field = vcompFieldForMessage(received_api);
117+
can_feedback_freq_status_->tick();
103118
} else if ((received_api & CAN_MSGID_API_M & CAN_API_MC_SPD) == CAN_API_MC_SPD) {
104119
field = spdFieldForMessage(received_api);
120+
can_feedback_freq_status_->tick();
105121
} else if ((received_api & CAN_MSGID_API_M & CAN_API_MC_VOLTAGE) == CAN_API_MC_VOLTAGE) {
106122
field = voltageFieldForMessage(received_api);
123+
can_feedback_freq_status_->tick();
107124
}
108125

109126
if (!field) {
@@ -1026,4 +1043,18 @@ Driver::Field * Driver::cfgFieldForMessage(uint32_t api)
10261043
return &cfg_fields_[cfg_field_index];
10271044
}
10281045

1046+
/**
1047+
* @brief Runs the frequency diagnostic update to populate the status message
1048+
*/
1049+
void Driver::runFreqStatus(diagnostic_updater::DiagnosticStatusWrapper & stat)
1050+
{
1051+
can_feedback_freq_status_->run(stat);
1052+
1053+
stat.add("Duty cycle", lastDutyCycle());
1054+
stat.add("Current (A)", lastCurrent());
1055+
stat.add("Speed (rad/s)", lastSpeed());
1056+
stat.add("Position", lastPosition());
1057+
stat.add("Setpoint", lastSetpoint());
1058+
}
1059+
10291060
} // namespace puma_motor_driver

clearpath_motor_drivers/puma_motor_driver/src/multi_puma_node.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTE
2121
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2222
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2323
*/
24+
#include <string>
25+
2426
#include "puma_motor_driver/multi_puma_node.hpp"
2527

2628
MultiPumaNode::MultiPumaNode(const std::string node_name)
2729
:Node(node_name),
2830
active_(false),
2931
status_count_(0),
30-
desired_mode_(clearpath_motor_msgs::msg::PumaStatus::MODE_SPEED)
32+
desired_mode_(clearpath_motor_msgs::msg::PumaStatus::MODE_SPEED),
33+
updater_(this)
3134
{
3235
// Parameters
3336
this->declare_parameter("canbus_dev", "vcan0");
@@ -118,6 +121,13 @@ MultiPumaNode::MultiPumaNode(const std::string node_name)
118121

119122
run_timer_ = this->create_wall_timer(
120123
std::chrono::milliseconds(1000 / freq_), std::bind(&MultiPumaNode::run, this));
124+
125+
// Setup diagnostics
126+
updater_.setHardwareID("Puma");
127+
for (uint8_t i = 0; i < joint_names_.size(); i++) {
128+
std::string name = "Puma Motor Driver " + std::to_string(i + 1) + " (" + joint_names_[i] + ")";
129+
updater_.add(name, std::bind(&MultiPumaNode::driverDiagnostic, this, std::placeholders::_1, i));
130+
}
121131
}
122132

123133
bool MultiPumaNode::getFeedback()
@@ -213,6 +223,40 @@ void MultiPumaNode::publishStatus()
213223
}
214224
}
215225

226+
/**
227+
* @brief Diagnostic task to report details for each motor driver
228+
*
229+
* @param i Driver index number
230+
*/
231+
void MultiPumaNode::driverDiagnostic(DiagnosticStatusWrapper & stat, int i)
232+
{
233+
// Assume we're OK. This will be merged over later on if we aren't
234+
stat.summary(DiagnosticStatusWrapper::OK, "OK");
235+
236+
drivers_[i].runFreqStatus(stat);
237+
238+
// basic stats
239+
stat.add("CAN ID", (int)status_msg_.drivers[i].device_number);
240+
stat.add("Joint Name", status_msg_.drivers[i].device_name);
241+
stat.add("Bus Voltage (V)", status_msg_.drivers[i].bus_voltage);
242+
stat.add("Motor Temperature (C)", status_msg_.drivers[i].temperature);
243+
stat.add("Output Voltage (V)", status_msg_.drivers[i].output_voltage);
244+
stat.add("Analogue Input (V)", status_msg_.drivers[i].analog_input);
245+
246+
// control mode; convert to a string
247+
stat.add("Mode", MODE_FLAG_LABELS_.at((int)status_msg_.drivers[i].mode));
248+
249+
// fault flags; these are a bit field
250+
for (auto label : FAULT_FLAG_LABELS_) {
251+
bool flag = (status_msg_.drivers[i].fault >> label.first) & 0x01;
252+
stat.add(label.second, flag ? "True" : "False");
253+
if (flag) {
254+
// raise a warning if there's a fault
255+
stat.mergeSummary(DiagnosticStatusWrapper::WARN, label.second);
256+
}
257+
}
258+
}
259+
216260
void MultiPumaNode::cmdCallback(const sensor_msgs::msg::JointState::SharedPtr msg)
217261
{
218262
if (active_) {

0 commit comments

Comments
 (0)