Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newest roboclaw_python_library and various changes #26

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
devel/
logs/
build/
bin/
lib/
msg_gen/
srv_gen/
msg/*Action.msg
msg/*ActionFeedback.msg
msg/*ActionGoal.msg
msg/*ActionResult.msg
msg/*Feedback.msg
msg/*Goal.msg
msg/*Result.msg
msg/_*.py
build_isolated/
devel_isolated/

# Generated by dynamic reconfigure
*.cfgc
/cfg/cpp/
/cfg/*.py

# Ignore generated docs
*.dox
*.wikidoc

# eclipse stuff
.project
.cproject

# qcreator stuff
CMakeLists.txt.user

srv/_*.py
*.pcd
*.pyc
qtcreator-*
*.user

/planning/cfg
/planning/docs
/planning/src

*~

# Emacs
.#*

# Catkin custom files
CATKIN_IGNORE
10 changes: 5 additions & 5 deletions roboclaw_node/launch/roboclaw.launch
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?xml version="1.0"?>
<launch>

<arg name="dev" default="/dev/ttyACM0"/>
<arg name="dev" default="/dev/roboclaw"/>
<arg name="baud" default="115200"/>
<arg name="address" default="128"/>
<arg name="max_speed" default="2.0"/>
<arg name="ticks_per_meter" default="4342.2"/>
<arg name="base_width" default="0.315"/>
<arg name="max_speed" default="1.0"/>
<arg name="ticks_per_meter" default="8130"/>
<arg name="base_width" default="0.2"/>
<arg name="run_diag" default="true"/>

<node if="$(arg run_diag)" pkg="roboclaw_node" type="roboclaw_node.py" name="roboclaw_node">
<node if="$(arg run_diag)" pkg="roboclaw_node" type="roboclaw_node.py" name="roboclaw_node" respawn="true" respawn_delay="5">
<param name="~dev" value="$(arg dev)"/>
<param name="~baud" value="$(arg baud)"/>
<param name="~address" value="$(arg address)"/>
Expand Down
123 changes: 80 additions & 43 deletions roboclaw_node/nodes/roboclaw_node.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python
from math import pi, cos, sin
from numbers import Number
import threading

import diagnostic_msgs
import diagnostic_updater
import roboclaw_driver.roboclaw_driver as roboclaw
from roboclaw_driver.roboclaw_driver import Roboclaw
import rospy
import tf
from geometry_msgs.msg import Quaternion, Twist
Expand Down Expand Up @@ -86,7 +88,7 @@ def publish_odom(self, cur_x, cur_y, cur_theta, vx, vth):

br = tf.TransformBroadcaster()
br.sendTransform((cur_x, cur_y, 0),
tf.transformations.quaternion_from_euler(0, 0, -cur_theta),
tf.transformations.quaternion_from_euler(0, 0, cur_theta),
current_time,
"base_link",
"odom")
Expand Down Expand Up @@ -119,23 +121,32 @@ def publish_odom(self, cur_x, cur_y, cur_theta, vx, vth):
class Node:
def __init__(self):

self.ERRORS = {0x0000: (diagnostic_msgs.msg.DiagnosticStatus.OK, "Normal"),
0x0001: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "M1 over current"),
0x0002: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "M2 over current"),
0x0004: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Emergency Stop"),
0x0008: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Temperature1"),
0x0010: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Temperature2"),
0x0020: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Main batt voltage high"),
0x0040: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Logic batt voltage high"),
0x0080: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Logic batt voltage low"),
0x0100: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "M1 driver fault"),
0x0200: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "M2 driver fault"),
0x0400: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Main batt voltage high"),
0x0800: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Main batt voltage low"),
0x1000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Temperature1"),
0x2000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Temperature2"),
0x4000: (diagnostic_msgs.msg.DiagnosticStatus.OK, "M1 home"),
0x8000: (diagnostic_msgs.msg.DiagnosticStatus.OK, "M2 home")}
# TODO better way to deal bit mask
self.ERRORS = {0x000000: (diagnostic_msgs.msg.DiagnosticStatus.OK, "Normal"),
0x000001: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "E-Stop"),
0x000002: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Temperature1"),
0x000004: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Temperature2"),
0x000008: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Main Voltage High"),
0x000010: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Logic Voltage High"),
0x000020: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "Logic Voltage Low"),
0x000040: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M1 Driver Fault"),
0x000080: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M2 Driver Fault"),
0x000100: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M1 Speed"),
0x000200: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M2 Speed"),
0x000400: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M1 Position"),
0x000800: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M2 Position"),
0x001000: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M1 Current"),
0x002000: (diagnostic_msgs.msg.DiagnosticStatus.ERROR, "M2 Current"),
0x010000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "M1 Over Current"),
0x020000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "M2 Over Current"),
0x040000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Main Voltage High"),
0x080000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Main Voltage Low"),
0x100000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Temperature1"),
0x200000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Temperature2"),
0x400000: (diagnostic_msgs.msg.DiagnosticStatus.OK, "S4 Signal Triggered"),
0x800000: (diagnostic_msgs.msg.DiagnosticStatus.OK, "S5 Signal Triggered"),
0x01000000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Speed Error Limit"),
0x02000000: (diagnostic_msgs.msg.DiagnosticStatus.WARN, "Position Error Limit")}

rospy.init_node("roboclaw_node")
rospy.on_shutdown(self.shutdown)
Expand All @@ -148,21 +159,33 @@ def __init__(self):
rospy.logfatal("Address out of range")
rospy.signal_shutdown("Address out of range")

self.roboclaw = Roboclaw(dev_name, baud_rate)
# TODO need someway to check if address is correct
try:
roboclaw.Open(dev_name, baud_rate)
self.roboclaw.Open()
except Exception as e:
rospy.logfatal("Could not connect to Roboclaw")
rospy.logdebug(e)
rospy.signal_shutdown("Could not connect to Roboclaw")

# We have a single 'roboclaw' object handling serial communications.
# We're about to launch different threads that each want to talk.
# 1 - Diagnostics thread calling into our self.check_vitals
# 2 - '/cmd_vel' thread calling our self.cmd_vel_callback
# 3 - our self.run that publishes to '/odom'
# To prevent thread collision in the middle of serial communication
# (which causes sync errors) all access to roboclaw from now
# must be synchronized using this mutually exclusive lock object.
self.mutex = threading.Lock()

self.updater = diagnostic_updater.Updater()
self.updater.setHardwareID("Roboclaw")
self.updater.add(diagnostic_updater.
FunctionDiagnosticTask("Vitals", self.check_vitals))

try:
version = roboclaw.ReadVersion(self.address)
with self.mutex:
version = self.roboclaw.ReadVersion(self.address)
except Exception as e:
rospy.logwarn("Problem getting roboclaw version")
rospy.logdebug(e)
Expand All @@ -173,11 +196,12 @@ def __init__(self):
else:
rospy.logdebug(repr(version[1]))

roboclaw.SpeedM1M2(self.address, 0, 0)
roboclaw.ResetEncoders(self.address)
with self.mutex:
self.roboclaw.SpeedM1M2(self.address, 0, 0)
self.roboclaw.ResetEncoders(self.address)

self.MAX_SPEED = float(rospy.get_param("~max_speed", "2.0"))
self.TICKS_PER_METER = float(rospy.get_param("~tick_per_meter", "4342.2"))
self.TICKS_PER_METER = float(rospy.get_param("~ticks_per_meter", "4342.2"))
self.BASE_WIDTH = float(rospy.get_param("~base_width", "0.315"))

self.encodm = EncoderOdom(self.TICKS_PER_METER, self.BASE_WIDTH)
Expand All @@ -202,8 +226,9 @@ def run(self):
if (rospy.get_rostime() - self.last_set_speed_time).to_sec() > 1:
rospy.loginfo("Did not get command for 1 second, stopping")
try:
roboclaw.ForwardM1(self.address, 0)
roboclaw.ForwardM2(self.address, 0)
with self.mutex:
self.roboclaw.ForwardM1(self.address, 0)
self.roboclaw.ForwardM2(self.address, 0)
except OSError as e:
rospy.logerr("Could not stop")
rospy.logdebug(e)
Expand All @@ -213,24 +238,26 @@ def run(self):
status2, enc2, crc2 = None, None, None

try:
status1, enc1, crc1 = roboclaw.ReadEncM1(self.address)
with self.mutex:
status1, enc1, crc1 = self.roboclaw.ReadEncM1(self.address)
except ValueError:
pass
except OSError as e:
rospy.logwarn("ReadEncM1 OSError: %d", e.errno)
rospy.logdebug(e)

try:
status2, enc2, crc2 = roboclaw.ReadEncM2(self.address)
with self.mutex:
status2, enc2, crc2 = self.roboclaw.ReadEncM2(self.address)
except ValueError:
pass
except OSError as e:
rospy.logwarn("ReadEncM2 OSError: %d", e.errno)
rospy.logdebug(e)

if ('enc1' in vars()) and ('enc2' in vars()):
if (isinstance(enc1,Number) and isinstance(enc2,Number)):
rospy.logdebug(" Encoders %d %d" % (enc1, enc2))
self.encodm.update_publish(enc1, enc2)
self.encodm.update_publish(enc2, enc1) # update_publish(enc_left, enc_right)

self.updater.update()
r_time.sleep()
Expand All @@ -255,29 +282,37 @@ def cmd_vel_callback(self, twist):
try:
# This is a hack way to keep a poorly tuned PID from making noise at speed 0
if vr_ticks is 0 and vl_ticks is 0:
roboclaw.ForwardM1(self.address, 0)
roboclaw.ForwardM2(self.address, 0)
with self.mutex:
self.roboclaw.ForwardM1(self.address, 0)
self.roboclaw.ForwardM2(self.address, 0)
else:
roboclaw.SpeedM1M2(self.address, vr_ticks, vl_ticks)
with self.mutex:
self.roboclaw.SpeedM1M2(self.address, vr_ticks, vl_ticks)
except OSError as e:
rospy.logwarn("SpeedM1M2 OSError: %d", e.errno)
rospy.logdebug(e)

# TODO: Need to make this work when more than one error is raised
def check_vitals(self, stat):
try:
status = roboclaw.ReadError(self.address)[1]
with self.mutex:
status = self.roboclaw.ReadError(self.address)[1]
except OSError as e:
rospy.logwarn("Diagnostics OSError: %d", e.errno)
rospy.logdebug(e)
return
state, message = self.ERRORS[status]
try:
state, message = self.ERRORS[status]
except KeyError:
state = diagnostic_msgs.msg.DiagnosticStatus.ERROR
message = "Unknown or various errors: 0x{0:x}".format(status)
stat.summary(state, message)
try:
stat.add("Main Batt V:", float(roboclaw.ReadMainBatteryVoltage(self.address)[1] / 10))
stat.add("Logic Batt V:", float(roboclaw.ReadLogicBatteryVoltage(self.address)[1] / 10))
stat.add("Temp1 C:", float(roboclaw.ReadTemp(self.address)[1] / 10))
stat.add("Temp2 C:", float(roboclaw.ReadTemp2(self.address)[1] / 10))
with self.mutex:
stat.add("Main Batt V:", float(self.roboclaw.ReadMainBatteryVoltage(self.address)[1]) / 10)
stat.add("Logic Batt V:", float(self.roboclaw.ReadLogicBatteryVoltage(self.address)[1]) / 10)
stat.add("Temp1 C:", float(self.roboclaw.ReadTemp(self.address)[1]) / 10)
stat.add("Temp2 C:", float(self.roboclaw.ReadTemp2(self.address)[1]) / 10)
except OSError as e:
rospy.logwarn("Diagnostics OSError: %d", e.errno)
rospy.logdebug(e)
Expand All @@ -287,13 +322,15 @@ def check_vitals(self, stat):
def shutdown(self):
rospy.loginfo("Shutting down")
try:
roboclaw.ForwardM1(self.address, 0)
roboclaw.ForwardM2(self.address, 0)
with self.mutex:
self.roboclaw.ForwardM1(self.address, 0)
self.roboclaw.ForwardM2(self.address, 0)
except OSError:
rospy.logerr("Shutdown did not work trying again")
try:
roboclaw.ForwardM1(self.address, 0)
roboclaw.ForwardM2(self.address, 0)
with self.mutex:
self.roboclaw.ForwardM1(self.address, 0)
self.roboclaw.ForwardM2(self.address, 0)
except OSError as e:
rospy.logerr("Could not shutdown motors!!!!")
rospy.logdebug(e)
Expand Down
Binary file removed roboclaw_node/nodes/roboclaw_node.pyc
Binary file not shown.
22 changes: 11 additions & 11 deletions roboclaw_node/package.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<package>
<package format="2">
<name>roboclaw_node</name>
<version>0.0.1</version>
<description>Node for roboclaw</description>
Expand All @@ -8,6 +8,7 @@
<!-- Example: -->
<!-- <maintainer email="[email protected]">Jane Doe</maintainer> -->
<maintainer email="[email protected]">Brad Bazemore</maintainer>
<maintainer email="[email protected]">Uwe</maintainer>


<!-- One license tag required, multiple allowed, one license per tag -->
Expand All @@ -26,6 +27,7 @@
<!-- Authors do not have to be maintianers, but could be -->
<!-- Example: -->
<author email="[email protected]">Brad Bazemore</author>
<author email="[email protected]">Uwe</author>


<!-- The *_depend tags are used to specify dependencies -->
Expand All @@ -40,16 +42,14 @@
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>geometry_msgs</build_depend>
<build_depend>nav_msgs</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>tf</build_depend>
<run_depend>geometry_msgs</run_depend>
<run_depend>nav_msgs</run_depend>
<run_depend>rospy</run_depend>
<run_depend>std_msgs</run_depend>
<run_depend>tf</run_depend>
<depend>geometry_msgs</depend>
<depend>nav_msgs</depend>
<depend>rospy</depend>
<depend>std_msgs</depend>
<depend>tf</depend>
<depend>diagnostic_updater</depend>
<depend>diagnostic_aggregator</depend>
<depend>python-serial</depend>


<!-- The export tag contains other, unspecified, tags -->
Expand Down
Loading