motor_hardware.cc
Go to the documentation of this file.
1 
32 #include <boost/assign.hpp>
33 
34 #include <boost/math/special_functions/round.hpp>
35 
36 //#define SENSOR_DISTANCE 0.002478
37 
38 // For experimental purposes users will see that the wheel encoders are three phases
39 // of very neaar 43 pulses per revolution or about 43*3 edges so we see very about 129 ticks per rev
40 // This leads to 129/(2*Pi) or about 20.53 ticks per radian experimentally.
41 // Below we will go with the exact ratio from gearbox specs
42 // 60 ticks per revolution of the motor (pre gearbox)
43 // 17.2328767123 and gear ratio of 4.29411764706:1
44 #define TICKS_PER_RADIAN_ENC_3_STATE (20.50251516) // used to read more misleading value of (41.0058030317/2)
45 #define QTICKS_PER_RADIAN (ticks_per_radian*4) // Quadrature ticks makes code more readable later
46 
47 #define VELOCITY_READ_PER_SECOND \
48  10.0 // read = ticks / (100 ms), so we have scale of 10 for ticks/second
49 #define LOWEST_FIRMWARE_VERSION 28
50 
51 // Debug verification use only
52 int32_t g_odomLeft = 0;
53 int32_t g_odomRight = 0;
54 int32_t g_odomEvent = 0;
55 
58  ros::V_string joint_names =
59  boost::assign::list_of("left_wheel_joint")("right_wheel_joint");
60 
61  for (size_t i = 0; i < joint_names.size(); i++) {
62  hardware_interface::JointStateHandle joint_state_handle(
63  joint_names[i], &joints_[i].position, &joints_[i].velocity,
64  &joints_[i].effort);
65  joint_state_interface_.registerHandle(joint_state_handle);
66 
68  joint_state_handle, &joints_[i].velocity_command);
70  }
73 
75  new MotorSerial(serial_params.serial_port, serial_params.baud_rate);
76 
77  leftError = nh.advertise<std_msgs::Int32>("left_error", 1);
78  rightError = nh.advertise<std_msgs::Int32>("right_error", 1);
79 
80  battery_state = nh.advertise<sensor_msgs::BatteryState>("battery_state", 1);
81  motor_power_active = nh.advertise<std_msgs::Bool>("motor_power_active", 1);
82 
83  sendPid_count = 0;
84 
85  estop_motor_power_off = false; // Keeps state of ESTOP switch where true is in ESTOP state
86 
87  // Save hardware encoder specifics for ticks in one radian of rotation of main wheel
89 
91 
100  prev_fw_params.max_pwm = -1;
109  prev_fw_params.max_pwm = -1;
110 
111  hardware_version = 0;
112  firmware_version = 0;
113  firmware_date = 0;
114 
115  diag_updater.setHardwareID("Motor Controller");
122 }
123 
125 
127  for (size_t i = 0; i < sizeof(joints_) / sizeof(joints_[0]); i++) {
128  joints_[i].velocity_command = 0;
129  }
130 }
131 
132 // readInputs() will receive serial and act on the response from motor controller
133 //
134 // The motor controller sends unsolicited messages periodically so we must read the
135 // messages to update status in near realtime
136 //
138  while (motor_serial_->commandAvailable()) {
139  MotorMessage mm;
141  if (mm.getType() == MotorMessage::TYPE_RESPONSE) {
142  switch (mm.getRegister()) {
144  if (mm.getData() < LOWEST_FIRMWARE_VERSION) {
145  ROS_FATAL("Firmware version %d, expect %d or above",
147  throw std::runtime_error("Firmware version too low");
148  } else {
149  ROS_INFO("Firmware version %d", mm.getData());
150  firmware_version = mm.getData();
152  }
153  break;
154 
156  // Firmware date is only supported as of fw version MIN_FW_FIRMWARE_DATE
157  ROS_INFO("Firmware date 0x%x (format 0xYYYYMMDD)", mm.getData());
158  firmware_date = mm.getData();
160  break;
161 
163  // These counts are the incremental number of ticks since last report
164  // WARNING: IF WE LOOSE A MESSAGE WE DRIFT FROM REAL POSITION
165  int32_t odom = mm.getData();
166  // ROS_ERROR("odom signed %d", odom);
167  int16_t odomLeft = (odom >> 16) & 0xffff;
168  int16_t odomRight = odom & 0xffff;
169 
170  // Debug code to be used for verification
171  g_odomLeft += odomLeft;
172  g_odomRight += odomRight;
173  g_odomEvent += 1;
174  //if ((g_odomEvent % 50) == 1) { ROS_ERROR("leftOdom %d rightOdom %d", g_odomLeft, g_odomRight); }
175 
176  // Add or subtract from position using the incremental odom value
177  joints_[0].position += (odomLeft / ticks_per_radian);
178  joints_[1].position += (odomRight / ticks_per_radian);
179 
180  motor_diag_.odom_update_status.tick(); // Let diag know we got odom
181  break;
182  }
184  std_msgs::Int32 left;
185  std_msgs::Int32 right;
186  int32_t speed = mm.getData();
187  int16_t leftSpeed = (speed >> 16) & 0xffff;
188  int16_t rightSpeed = speed & 0xffff;
189 
190  left.data = leftSpeed;
191  right.data = rightSpeed;
192  leftError.publish(left);
193  rightError.publish(right);
194  break;
195  }
197  int32_t data = mm.getData();
198 
199  // Enable or disable hardware options reported from firmware
201 
202  // Set radians per encoder tic
203  if (data & MotorMessage::OPT_ENC_6_STATE) {
206  } else {
207  fw_params.hw_options &= ~MotorMessage::OPT_ENC_6_STATE;
209  }
210  break;
211  }
213  int32_t data = mm.getData();
214 
215  if (data & MotorMessage::LIM_M1_PWM) {
216  ROS_WARN("left PWM limit reached");
217  motor_diag_.left_pwm_limit = true;
218  }
219  if (data & MotorMessage::LIM_M2_PWM) {
220  ROS_WARN("right PWM limit reached");
222  }
223  if (data & MotorMessage::LIM_M1_INTEGRAL) {
224  ROS_DEBUG("left Integral limit reached");
226  }
227  if (data & MotorMessage::LIM_M2_INTEGRAL) {
228  ROS_DEBUG("right Integral limit reached");
230  }
231  if (data & MotorMessage::LIM_M1_MAX_SPD) {
232  ROS_WARN("left Maximum speed reached");
234  }
235  if (data & MotorMessage::LIM_M2_MAX_SPD) {
236  ROS_WARN("right Maximum speed reached");
238  }
239  if (data & MotorMessage::LIM_PARAM_LIMIT) {
240  ROS_WARN_ONCE("parameter limit in firmware");
242  }
243  break;
244  }
246  int32_t data = mm.getData();
247  sensor_msgs::BatteryState bstate;
248  bstate.voltage = (float)data * fw_params.battery_voltage_multiplier +
250  bstate.current = std::numeric_limits<float>::quiet_NaN();
251  bstate.charge = std::numeric_limits<float>::quiet_NaN();
252  bstate.capacity = std::numeric_limits<float>::quiet_NaN();
253  bstate.design_capacity = std::numeric_limits<float>::quiet_NaN();
254  bstate.percentage = std::max(0.0, std::min(1.0, (bstate.voltage - 20.0) * 0.125));
255  bstate.power_supply_status = sensor_msgs::BatteryState::POWER_SUPPLY_STATUS_UNKNOWN;
256  bstate.power_supply_health = sensor_msgs::BatteryState::POWER_SUPPLY_HEALTH_UNKNOWN;
257  bstate.power_supply_technology = sensor_msgs::BatteryState::POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
258  battery_state.publish(bstate);
259 
260  motor_diag_.battery_voltage = bstate.voltage;
261  break;
262  }
263  case MotorMessage::REG_MOT_PWR_ACTIVE: { // Starting with rev 5.0 board we can see power state
264  int32_t data = mm.getData();
265 
266  if (data & MotorMessage::MOT_POW_ACTIVE) {
267  if (estop_motor_power_off == true) {
268  ROS_WARN("Motor power has gone from inactive to active. Most likely from ESTOP switch");
269  }
270  estop_motor_power_off = false;
271  } else {
272  if (estop_motor_power_off == false) {
273  ROS_WARN("Motor power has gone inactive. Most likely from ESTOP switch active");
274  }
275  estop_motor_power_off = true;
276  }
277  motor_diag_.estop_motor_power_off = estop_motor_power_off; // A copy for diagnostics topic
278 
279  std_msgs::Bool estop_message;
280  estop_message.data = !estop_motor_power_off;
281  motor_power_active.publish(estop_message);
282  }
283  default:
284  break;
285  }
286  }
287  }
288 }
289 
290 // writeSpeedsInRadians() Take in radians per sec for wheels and send in message to controller
291 //
292 // A direct write speeds that allows caller setting speeds in radians
293 // This interface allows maintaining of system speed in state but override to zero
294 // which is of value for such a case as ESTOP implementation
295 //
296 void MotorHardware::writeSpeedsInRadians(double left_radians, double right_radians) {
297  MotorMessage both;
300 
301  int16_t left_speed = calculateSpeedFromRadians(left_radians);
302  int16_t right_speed = calculateSpeedFromRadians(right_radians);
303 
304  // The masking with 0x0000ffff is necessary for handling -ve numbers
305  int32_t data = (left_speed << 16) | (right_speed & 0x0000ffff);
306  both.setData(data);
307 
308  std_msgs::Int32 smsg;
309  smsg.data = left_speed;
310 
312 
313  // ROS_ERROR("velocity_command %f rad/s %f rad/s",
314  // joints_[0].velocity_command, joints_[1].velocity_command);
315  // ROS_ERROR("SPEEDS %d %d", left.getData(), right.getData());
316 }
317 
318 // writeSpeeds() Take in radians per sec for wheels and send in message to controller
319 //
320 // Legacy interface where no speed overrides are supported
321 //
323  // This call pulls in speeds from the joints array maintained by other layers
324 
325  double left_radians = joints_[0].velocity_command;
326  double right_radians = joints_[1].velocity_command;
327 
328  writeSpeedsInRadians(left_radians, right_radians);
329 }
330 
332  MotorMessage fw_version_msg;
334  fw_version_msg.setType(MotorMessage::TYPE_READ);
335  fw_version_msg.setData(0);
336  motor_serial_->transmitCommand(fw_version_msg);
337 }
338 
339 // Firmware date register implemented as of MIN_FW_FIRMWARE_DATE
341  MotorMessage fw_date_msg;
343  fw_date_msg.setType(MotorMessage::TYPE_READ);
344  fw_date_msg.setData(0);
345  motor_serial_->transmitCommand(fw_date_msg);
346 }
347 
348 
349 // Due to greatly limited pins on the firmware processor the host figures out the hardware rev and sends it to fw
350 // The hardware version is 0x0000MMmm where MM is major rev like 4 and mm is minor rev like 9 for first units.
351 // The 1st firmware version this is set for is 32, before it was always 1
353  ROS_INFO("setting hardware_version to %x", (int)hardware_version);
354  MotorMessage mm;
357  mm.setData(hardware_version);
359 }
360 
361 // Setup the controller board threshold to put into force estop protection on boards prior to rev 5.0 with hardware support
363  ROS_INFO("setting Estop PID threshold to %d", (int)estop_pid_threshold);
364  MotorMessage mm;
367  mm.setData(estop_pid_threshold);
369 }
370 
371 // Setup the controller board to have estop button state detection feature enabled or not
372 void MotorHardware::setEstopDetection(int32_t estop_detection) {
373  ROS_INFO("setting estop button detection to %x", (int)estop_detection);
374  MotorMessage mm;
377  mm.setData(estop_detection);
379 }
380 
381 // Returns true if estop switch is active OR if motor power is off somehow off
383  return estop_motor_power_off;
384 }
385 
386 // Setup the controller board maximum settable motor forward speed
388  ROS_INFO("setting max motor forward speed to %d", (int)max_speed_fwd);
389  MotorMessage mm;
392  mm.setData(max_speed_fwd);
394 }
395 
396 // Setup the controller board maximum settable motor reverse speed
398  ROS_INFO("setting max motor reverse speed to %d", (int)max_speed_rev);
399  MotorMessage mm;
402  mm.setData(max_speed_rev);
404 }
405 
406 // Setup the controller board maximum PWM level allowed for a motor
408  ROS_INFO("setting max motor PWM to %x", (int)max_pwm);
409  MotorMessage mm;
412  mm.setData(max_pwm);
414 }
415 
417  ROS_ERROR("setting deadman to %d", (int)deadman_timer);
418  MotorMessage mm;
421  mm.setData(deadman_timer);
423 }
424 
425 void MotorHardware::setDeadzoneEnable(int32_t deadzone_enable) {
426  ROS_ERROR("setting deadzone enable to %d", (int)deadzone_enable);
427  MotorMessage mm;
432 }
433 
443 }
444 
446  std::vector<MotorMessage> commands;
447 
448  // ROS_ERROR("sending PID %d %d %d %d",
449  //(int)p_value, (int)i_value, (int)d_value, (int)denominator_value);
450 
451  // Only send one register at a time to avoid overwhelming serial comms
452  // SUPPORT NOTE! Adjust modulo for total parameters in the cycle
453  // and be sure no duplicate modulos are used!
454  int cycle = (sendPid_count++) % 6; // MUST BE THE TOTAL NUMBER IN THIS HANDLING
455 
456  if (cycle == 0 &&
458  ROS_WARN("Setting P to %d", fw_params.pid_proportional);
460  MotorMessage p;
464  commands.push_back(p);
465  }
466 
467  if (cycle == 1 && fw_params.pid_integral != prev_fw_params.pid_integral) {
468  ROS_WARN("Setting I to %d", fw_params.pid_integral);
470  MotorMessage i;
474  commands.push_back(i);
475  }
476 
477  if (cycle == 2 &&
479  ROS_WARN("Setting D to %d", fw_params.pid_derivative);
481  MotorMessage d;
485  commands.push_back(d);
486  }
487 
488  if (cycle == 3 && (motor_diag_.firmware_version >= MIN_FW_PID_V_TERM) &&
490  ROS_WARN("Setting V to %d", fw_params.pid_velocity);
492  MotorMessage v;
496  commands.push_back(v);
497  }
498 
499  if (cycle == 4 &&
501  ROS_WARN("Setting Denominator to %d", fw_params.pid_denominator);
503  MotorMessage denominator;
505  denominator.setType(MotorMessage::TYPE_WRITE);
506  denominator.setData(fw_params.pid_denominator);
507  commands.push_back(denominator);
508  }
509 
510  if (cycle == 5 &&
513  ROS_WARN("Setting D window to %d", fw_params.pid_moving_buffer_size);
516  MotorMessage winsize;
520  commands.push_back(winsize);
521  }
522 
523  // SUPPORT NOTE! Adjust max modulo for total parameters in the cycle, be sure no duplicates used!
524 
525  if (commands.size() != 0) {
526  motor_serial_->transmitCommands(commands);
527  }
528 }
529 
530 void MotorHardware::setDebugLeds(bool led_1, bool led_2) {
531  std::vector<MotorMessage> commands;
532 
533  MotorMessage led1;
536  if (led_1) {
537  led1.setData(0x00000001);
538  } else {
539  led1.setData(0x00000000);
540  }
541  commands.push_back(led1);
542 
543  MotorMessage led2;
546  if (led_2) {
547  led2.setData(0x00000001);
548  } else {
549  led2.setData(0x00000000);
550  }
551  commands.push_back(led2);
552 
553  motor_serial_->transmitCommands(commands);
554 }
555 
556 // calculate the binary speed value sent to motor controller board
557 // using an input expressed in radians.
558 // The firmware uses the same speed value no matter what type of encoder is used
559 int16_t MotorHardware::calculateSpeedFromRadians(double radians) const {
560  int16_t speed;
561  double encoderFactor = 1.0;
562 
563  // The firmware accepts same units for speed value
564  // and will deal with it properly depending on encoder handling in use
566  encoderFactor = 0.5;
567  }
568 
569  speed = boost::math::iround(encoderFactor * (radians * QTICKS_PER_RADIAN /
571  return speed;
572 }
573 
574 double MotorHardware::calculateRadiansFromTicks(int16_t ticks) const {
575  return (ticks * VELOCITY_READ_PER_SECOND / QTICKS_PER_RADIAN);
576 }
577 
578 // Diagnostics Status Updater Functions
580 using diagnostic_msgs::DiagnosticStatus;
581 
583  stat.add("Firmware Version", firmware_version);
584  if (firmware_version == 0) {
585  stat.summary(DiagnosticStatus::ERROR, "No firmware version reported. Power may be off.");
586  }
588  stat.summary(DiagnosticStatus::WARN, "Firmware is older than recommended! You must update firmware!");
589  }
590  else {
591  stat.summary(DiagnosticStatus::OK, "Firmware version is OK");
592  }
593 }
594 
596 
597  // Only output status if the firmware daycode is supported
599  std::stringstream stream;
600  stream << std::hex << firmware_date;
601  std::string daycode(stream.str());
602 
603  stat.add("Firmware Date", daycode);
604  stat.summary(DiagnosticStatus::OK, "Firmware daycode format is YYYYMMDD");
605  }
606 }
607 
608 // When a firmware limit condition is reported the diagnostic topic reports it.
609 // Once the report is made the condition is cleared till next time firmware reports that limit
611  stat.summary(DiagnosticStatus::OK, "Limits reached:");
612  if (left_pwm_limit) {
613  stat.mergeSummary(DiagnosticStatusWrapper::ERROR, " left pwm,");
614  left_pwm_limit = false;
615  }
616  if (right_pwm_limit) {
617  stat.mergeSummary(DiagnosticStatusWrapper::ERROR, " right pwm,");
618  right_pwm_limit = false;
619  }
620  if (left_integral_limit) {
621  stat.mergeSummary(DiagnosticStatusWrapper::WARN, " left integral,");
622  left_integral_limit = false;
623  }
624  if (right_integral_limit) {
625  stat.mergeSummary(DiagnosticStatusWrapper::WARN, " right integral,");
626  right_integral_limit = false;
627  }
628  if (left_max_speed_limit) {
629  stat.mergeSummary(DiagnosticStatusWrapper::WARN, " left speed,");
630  left_max_speed_limit = false;
631  }
632  if (right_max_speed_limit) {
633  stat.mergeSummary(DiagnosticStatusWrapper::WARN, " right speed,");
634  right_max_speed_limit = false;
635  }
636  if (param_limit_in_firmware) {
637  // A parameter was sent to firmware that was out of limits for the firmware register
638  stat.mergeSummary(DiagnosticStatusWrapper::WARN, " firmware limit,");
639  param_limit_in_firmware = false;
640  }
641 }
642 
644  stat.add("Battery Voltage", battery_voltage);
645  if (battery_voltage < 22.5) {
646  stat.summary(DiagnosticStatusWrapper::WARN, "Battery low");
647  }
648  else if (battery_voltage < 21.0) {
649  stat.summary(DiagnosticStatusWrapper::ERROR, "Battery critical");
650  }
651  else {
652  stat.summary(DiagnosticStatusWrapper::OK, "Battery OK");
653  }
654 }
656  stat.add("Motor Power", !estop_motor_power_off);
657  if (estop_motor_power_off == false) {
658  stat.summary(DiagnosticStatusWrapper::ERROR, "Motor power on");
659  }
660  else {
661  stat.summary(DiagnosticStatusWrapper::WARN, "Motor power off");
662  }
663 }
664 // motor_encoder_mode returns 0 for legacy encoders and 1 for 6-state firmware
666  stat.add("Firmware Options", firmware_options);
668  stat.summary(DiagnosticStatusWrapper::OK, "High resolution encoders");
669  }
670  else {
671  stat.summary(DiagnosticStatusWrapper::OK, "Standard resolution encoders");
672  }
673 }
674 
675 
d
void limit_status(diagnostic_updater::DiagnosticStatusWrapper &stat)
int32_t g_odomRight
int32_t pid_proportional
bool getEstopState(void)
int32_t pid_velocity
#define MIN_FW_FIRMWARE_DATE
Definition: motor_message.h:48
int32_t deadman_timer
#define ROS_FATAL(...)
void setEstopDetection(int32_t estop_detection)
void setHardwareVersion(int32_t hardware_version)
#define LOWEST_FIRMWARE_VERSION
double ticks_per_radian
MotorDiagnostics motor_diag_
ros::Publisher battery_state
virtual ~MotorHardware()
float battery_voltage_offset
void summary(unsigned char lvl, const std::string s)
int32_t pid_denominator
void setHardwareID(const std::string &hwid)
void setData(int32_t data)
void battery_status(diagnostic_updater::DiagnosticStatusWrapper &stat)
int32_t baud_rate
int transmitCommand(MotorMessage command)
Definition: motor_serial.cc:47
hardware_interface::JointStateInterface joint_state_interface_
void add(const std::string &name, TaskFunction f)
void requestFirmwareDate()
data
int32_t pid_moving_buffer_size
int transmitCommands(const std::vector< MotorMessage > &commands)
Definition: motor_serial.cc:55
int32_t g_odomLeft
int16_t calculateSpeedFromRadians(double radians) const
#define ROS_WARN(...)
bool estop_motor_power_off
ros::Publisher rightError
ros::Publisher motor_power_active
int commandAvailable()
Definition: motor_serial.cc:74
void setDeadzoneEnable(int32_t deadzone_enable)
void firmware_status(diagnostic_updater::DiagnosticStatusWrapper &stat)
static FirmwareParams firmware_params
Definition: motor_node.cc:44
int32_t controller_board_version
MotorMessage::MessageTypes getType() const
void publish(const boost::shared_ptr< M > &message) const
struct MotorHardware::Joint joints_[2]
std::vector< std::string > V_string
void setMaxRevSpeed(int32_t max_speed_rev)
int32_t pid_integral
#define ROS_WARN_ONCE(...)
int32_t g_odomEvent
#define ROS_INFO(...)
void setDeadmanTimer(int32_t deadman)
#define VELOCITY_READ_PER_SECOND
int32_t pid_derivative
int32_t estop_pid_threshold
double calculateRadiansFromTicks(int16_t ticks) const
float battery_voltage_multiplier
void setDebugLeds(bool led1, bool led2)
int32_t deadzone_enable
void setParams(FirmwareParams firmware_params)
ros::Publisher leftError
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void motor_power_status(diagnostic_updater::DiagnosticStatusWrapper &stat)
int32_t sendPid_count
int32_t max_speed_rev
void registerHandle(const JointStateHandle &handle)
FirmwareParams fw_params
void requestFirmwareVersion()
static CommsParams serial_params
Definition: motor_node.cc:45
#define MIN_FW_PID_V_TERM
Definition: motor_message.h:50
void mergeSummary(unsigned char lvl, const std::string s)
int32_t getData() const
void setMaxPwm(int32_t max_pwm)
void setRegister(MotorMessage::Registers reg)
int32_t max_speed_fwd
#define MIN_FW_RECOMMENDED
Definition: motor_message.h:42
void setMaxFwdSpeed(int32_t max_speed_fwd)
hardware_interface::VelocityJointInterface velocity_joint_interface_
#define TICKS_PER_RADIAN_ENC_3_STATE
#define QTICKS_PER_RADIAN
void setType(MotorMessage::MessageTypes type)
void setEstopPidThreshold(int32_t estop_pid_threshold)
MotorMessage receiveCommand()
Definition: motor_serial.cc:66
bool param_limit_in_firmware
int32_t deadman_timer
diagnostic_updater::FrequencyStatus odom_update_status
void firmware_date_status(diagnostic_updater::DiagnosticStatusWrapper &stat)
std::string serial_port
void add(const std::string &key, const T &val)
MotorSerial * motor_serial_
#define ROS_ERROR(...)
int32_t estop_detection
void firmware_options_status(diagnostic_updater::DiagnosticStatusWrapper &stat)
MotorMessage::Registers getRegister() const
diagnostic_updater::Updater diag_updater
void writeSpeedsInRadians(double left_radians, double right_radians)
#define ROS_DEBUG(...)
FirmwareParams prev_fw_params
MotorHardware(ros::NodeHandle nh, CommsParams serial_params, FirmwareParams firmware_params)
static const int32_t MOT_POW_ACTIVE


ubiquity_motor
Author(s):
autogenerated on Mon Feb 28 2022 23:57:06