4 from geometry_msgs.msg
import Twist, Quaternion
5 from threading
import Lock
6 from diagnostic_msgs.msg
import DiagnosticArray, DiagnosticStatus, KeyValue
7 from nav_msgs.msg
import Odometry
10 from std_msgs.msg
import Bool
14 self.
_node = rospy.init_node(
'Rover_Zero_Controller', anonymous=
True)
17 self.
_port = rospy.get_param(
'~dev',
'/dev/ttyACM0')
18 self.
_address = rospy.get_param(
'~address', 128)
19 self.
_baud = rospy.get_param(
'~baud', 115200)
20 self.
_max_vel = rospy.get_param(
'~max_vel', 5.0)
22 self.
_duty_coef = rospy.get_param(
'~speed_to_duty_coef', 1.02)
31 self.
_m1_v_p = rospy.get_param(
'~m1_v_p', 3.00)
32 self.
_m1_v_i = rospy.get_param(
'~m1_v_i', 0.35)
33 self.
_m1_v_d = rospy.get_param(
'~m1_v_d', 0.00)
34 self.
_m1_v_qpps = int(rospy.get_param(
'~m1_v_qpps', 10000))
35 self.
_m2_v_p = rospy.get_param(
'~m2_v_p', 3.00)
36 self.
_m2_v_i = rospy.get_param(
'~m2_v_i', 0.35)
37 self.
_m2_v_d = rospy.get_param(
'~m2_v_d', 0.00)
38 self.
_m2_v_qpps = int(rospy.get_param(
'~m2_v_qpps', 10000))
50 if not self._roboclaw.Open():
51 rospy.logfatal(
'Could not open serial at ' + self.
_port)
82 self.
_pub_diag = rospy.Publisher(
'/diagnostics', DiagnosticArray, queue_size=1)
84 self.
_pub_odom = rospy.Publisher(
'/odom', Odometry, queue_size=1)
106 self._roboclaw.ResetEncoders(self.
_address)
122 self._roboclaw.WriteNVM(self.
_address)
125 (res, p, i, d, qpps) = self._roboclaw.ReadM1VelocityPID(self.
_address)
131 rospy.loginfo(
'Motor 1 (P, I, D, QPPS): %f, %f, %f, %d', p, i, d, qpps)
133 (res, p, i, d, qpps) = self._roboclaw.ReadM2VelocityPID(self.
_address)
139 rospy.loginfo(
'Motor 2 (P, I, D, QPPS): %f, %f, %f, %d', p, i, d, qpps)
142 fatal_misconfiguration =
False 145 rospy.logfatal(
'Maximum velocity (max_vel) must be greater than 0.')
146 fatal_misconfiguration =
True 149 rospy.logfatal(
'Maximum turn rate (max_turn_rate) must be greater than 0.')
150 fatal_misconfiguration =
True 153 rospy.logfatal(
'The wheel velocity to motor duty coefficient (speed_to_duty_coef) must be greater than 0.')
154 fatal_misconfiguration =
True 157 rospy.logfatal(
'Encoder pulses per turn (encoder_pulses_per_turn) must be greater than 0.')
158 fatal_misconfiguration =
True 161 rospy.logfatal(
'Motor command frequency (motor_cmd_frequency_hz) must be greater than or equal to 1Hz.')
162 fatal_misconfiguration =
True 164 rospy.logwarn(
'Motor command frequency (motor_cmd_frequency_hz) is low. It is suggested that motor command update rate of at least 5Hz.')
167 rospy.logwarn(
'cmd_vel timeout (cmd_vel_timeout) disabled. Robot will execute last cmd_vel message forever.')
170 rospy.logwarn(
'Encoder odometry are not enabled (enable_encoder_odom).')
175 rospy.logfatal(
'Invalid user specified PID parameters for left motor.')
176 fatal_misconfiguration =
True 178 (res, p, i, d, qpps) = self._roboclaw.ReadM1VelocityPID(self.
_address)
179 if (p == 0.0
and i == 0.0
and d == 0.0)
or qpps <= 0:
180 rospy.logfatal(
'Invalid PID parameters for left motor saved on Roboclaw.')
181 fatal_misconfiguration =
True 186 rospy.logfatal(
'Invalid user specified PID parameters for right motor.')
187 fatal_misconfiguration =
True 189 (res, p, i, d, qpps) = self._roboclaw.ReadM2VelocityPID(self.
_address)
190 if (p == 0.0
and i == 0.0
and d == 0.0)
or qpps <= 0:
191 rospy.logfatal(
'Invalid PID parameters for right motor saved on Roboclaw.')
192 fatal_misconfiguration =
True 195 rospy.logwarn(
'PID feedback controls are not enabled (enable_esc_feedback_controls), running in duty cycles mode.')
196 rospy.logwarn(
'BEWARE: In duty cycle mode if the serial connection is lost it will indefitely execute the last command sent to the Roboclaw.')
199 rospy.loginfo(
'New motor controller settings will be saved to Roboclaw eeprom')
202 rospy.logfatal(
'Maximum current for left motor (left_motor_max_current) must be greater than 0')
203 fatal_misconfiguration =
True 206 rospy.logfatal(
'Maximum current for right motor (right_motor_max_current) must be greater than 0')
207 fatal_misconfiguration =
True 210 rospy.logwarn(
'Active breaking timeout (active_brake_timeout) is disabled. If PID control is enabled and active breaking is disabled when idle, passive current may be applied motors causing potential motor overheating or reduced battery life.')
213 rospy.logfatal(
'Invalid baselink frame: \'%s\'', self.
_odom_frame)
214 fatal_misconfiguration =
True 218 fatal_misconfiguration =
True 221 rospy.logfatal(
"Wheel base (wheel_base) must be greater than 0")
222 fatal_misconfiguration =
True 225 rospy.logfatal(
"Wheel radius (wheel_radius) must be greater than 0")
226 fatal_misconfiguration =
True 228 if fatal_misconfiguration:
229 rospy.signal_shutdown(
'Fatal Misconfiguration')
243 self._variable_lock.acquire()
245 self._variable_lock.release()
249 self._variable_lock.acquire()
251 self._variable_lock.release()
259 left_ = (linear_rate - 0.5 * self.
_wheel_base * angular_rate)
260 right_ = (linear_rate + 0.5 * self.
_wheel_base * angular_rate)
265 self._variable_lock.acquire()
267 self._variable_lock.release()
270 if left_speed != 0.0
or right_speed != 0.0:
292 return int(pulse_rate)
304 self._serial_lock.acquire()
305 self._roboclaw.DutyM1M2(self.
_address, left_duty, right_duty)
306 self._serial_lock.release()
309 """The Roboclaw does not have a serial timeout functionality. To make the robot safer we use the velocity with distance command to limit the distance the robot travels if the controller loses connection to the driver node""" 311 dt = 2.0
if dt > 2.0
else dt
312 self._serial_lock.acquire()
313 self._roboclaw.SpeedDistanceM1M2(self.
_address, left_velocity, int(dt * left_velocity),
314 right_velocity, int(dt * right_velocity), 1)
315 self._serial_lock.release()
318 now = rospy.get_rostime()
319 if now.to_sec() - self._last_cmd_vel_received.to_sec() > self.
_cmd_vel_timeout:
320 self._variable_lock.acquire()
322 self._variable_lock.release()
325 self._serial_lock.acquire()
328 self._serial_lock.release()
330 update_time = rospy.Time.now()
334 linear_vel = 0.5 * (left_speed + right_speed)
335 turn_vel = right_speed - left_speed
342 odom_msg = Odometry()
345 odom_msg.header.stamp = rospy.Time.now()
351 odom_msg.pose.pose.orientation.x = quat[0]
352 odom_msg.pose.pose.orientation.y = quat[1]
353 odom_msg.pose.pose.orientation.z = quat[2]
354 odom_msg.pose.pose.orientation.w = quat[3]
355 odom_msg.pose.covariance[0] = 1e-2
356 odom_msg.pose.covariance[7] = 1e-2
357 odom_msg.pose.covariance[35] = 1e-2
359 odom_msg.twist.twist.linear.x = linear_vel
360 odom_msg.twist.twist.angular.z = alpha_dot
361 odom_msg.twist.covariance[0] = 1e-3
362 odom_msg.twist.covariance[35] = 1e-4
363 self._pub_odom.publish(odom_msg)
368 darr = DiagnosticArray()
369 darr.header.stamp = rospy.get_rostime()
371 DiagnosticStatus(name=
"Roboclaw Driver",
375 KeyValue(key=
'Left Motor Max Current', value=
'{MAX_CURRENT} A'.format(MAX_CURRENT=str(self.
_left_motor_max_current))),
376 KeyValue(key=
'Left Motor Current', value=
'{CURRENT} A'.format(CURRENT=str(self.
_left_motor_current))),
377 KeyValue(key=
'Left Motor Max Current', value=
'{MAX_CURRENT} A'.format(MAX_CURRENT=str(self.
_right_motor_max_current))),
378 KeyValue(key=
'Right Motor Current', value=
'{CURRENT} A'.format(CURRENT=str(self.
_right_motor_current))),
379 KeyValue(key=
'Battery Voltage', value=
'{VOLTAGE}V'.format(VOLTAGE=str(self.
_battery_voltage))),
385 self._pub_diag.publish(darr)
392 self._serial_lock.acquire()
394 self._serial_lock.release()
397 self._serial_lock.acquire()
398 (res, m1_current, m2_current) = self._roboclaw.ReadCurrents(self.
_address)
399 self._serial_lock.release()
409 if __name__ ==
'__main__':
def _cmd_vel_timeout_cb(self, event)
def _configure_motor_controller(self)
def _motor_cmd_cb(self, event)
def send_motor_duty(self, left_duty, right_duty)
def verify_ros_parameters(self)
def _estop_enable_cb(self, estopstate)
def get_battery_voltage(self)
def _twist_cmd_cb(self, cmd)
def _diag_cb(self, event)
def get_motor_current(self)
_save_motor_controller_settings
def _diagnostics_update(self)
def send_motor_velocities(self, left_velocity, right_velocity)
def _estop_reset_cb(self, estopstate)
_esc_feedback_controls_enabled
_distance_per_encoder_pulse
def _twist_to_wheel_velocities(self, linear_rate, angular_rate)
def speed_to_pulse_rate(self, speed)
def speed_to_duty(self, speed)