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

Ros1 motor enable srv #113

Merged
merged 11 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 22 additions & 7 deletions panther_driver/src/driver_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from std_msgs.msg import Bool
from std_srvs.srv import Trigger, TriggerRequest, TriggerResponse

from panther_msgs.msg import DriverState, FaultFlag, ScriptFlag, RuntimeError

from panther_msgs.msg import DriverState, FaultFlag, IOState, RuntimeError, ScriptFlag
from panther_can import PantherCANSDO, PantherCANPDO
from panther_kinematics import PantherDifferential, PantherMecanum

Expand Down Expand Up @@ -96,6 +95,7 @@ def __init__(self, name: str) -> None:
self._driver_state_timer_period = 1.0 / 10.0 # freq. 10 Hz
self._safety_timer_period = 1.0 / 20.0 # freq. 20 Hz
self._time_last = rospy.Time.now()
self._motor_off_last_time = rospy.Time.now()
self._cmd_vel_command_last_time = rospy.Time.now()
self._cmd_vel_timeout = 0.2

Expand All @@ -107,6 +107,7 @@ def __init__(self, name: str) -> None:
self._motors_effort = [0.0, 0.0, 0.0, 0.0]

self._e_stop_cliented = False
self._motor_on = False
self._stop_cmd_vel_cb = True

# -------------------------------
Expand Down Expand Up @@ -217,6 +218,9 @@ def __init__(self, name: str) -> None:

self._cmd_vel_sub = rospy.Subscriber('/cmd_vel', Twist, self._cmd_vel_cb, queue_size=1)
self._e_stop_sub = rospy.Subscriber('hardware/e_stop', Bool, self._e_stop_cb, queue_size=1)
self._io_state_sub = rospy.Subscriber(
'hardware/io_state', IOState, self._io_state_cb, queue_size=1
)

# -------------------------------
# Service clients
Expand Down Expand Up @@ -351,11 +355,19 @@ def _safety_timer_cb(self, *args) -> None:
self._trigger_panther_e_stop()
self._stop_cmd_vel_cb = True

rospy.logerr_throttle(
10.0,
f'[{rospy.get_name()}] Unable to communicate with motor controllers (CAN interface connection failure). '
f'Please ensure that controllers are powered on.',
)
if not self._motor_on:
rospy.logwarn_throttle(
60.0, f'[{rospy.get_name()}] Motor controllers are not powered on'
)
self._motor_off_last_time = rospy.Time.now()
else:
# wait for motor drivers to power on before logging an error
if rospy.Time.now() - self._motor_off_last_time < rospy.Duration(2.0):
return
rospy.logerr_throttle(
10.0,
f'[{rospy.get_name()}] Unable to communicate with motor controllers (CAN interface connection failure)',
)
elif self._e_stop_cliented:
self._stop_cmd_vel_cb = True
else:
Expand All @@ -373,6 +385,9 @@ def _cmd_vel_cb(self, data: Twist) -> None:

self._cmd_vel_command_last_time = rospy.Time.now()

def _io_state_cb(self, io_state: IOState) -> None:
self._motor_on = io_state.motor_on

def _reset_roboteq_script_cb(self, req: TriggerRequest) -> TriggerResponse:
try:
if self._panther_can.restart_roboteq_script():
Expand Down
10 changes: 8 additions & 2 deletions panther_power_control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Node responsible for management of the safety board and the power board. Availab
- `charger_enabled` is related to service `charger_enable`,
- `digital_power` is related to service `digital_power_enable`,
- `fan` is related to service `fan_enable`,
- `motor_on` is related to service `motor_enable`,
- `power_button` indicates if power button is pressed.

#### Subscribes
Expand All @@ -31,7 +32,11 @@ Node responsible for management of the safety board and the power board. Availab
- `/panther/hardware/e_stop_reset` [*std_srvs/Trigger*]: reset emergency stop.
- `/panther/hardware/e_stop_trigger` [*std_srvs/Trigger*]: trigger emergency stop.
- `/panther/hardware/fan_enable` [*std_srvs/SetBool*]: enable or disable internal fan.
- `/panther/hardware/motors_enable` [*std_srvs/SetBool*]: enable or disable motor drivers.
- `/panther/hardware/motor_enable` [*std_srvs/SetBool*]: enable or disable motor drivers.

#### Service clients

- `/panther/driver/reset_roboteq_script` [*std_srvs/Trigger*]: used to reset Roboteq drivers script when enabling motor drivers.

### relays_node.py

Expand All @@ -40,7 +45,8 @@ This node is responsible for power management using relays. Available in Panther
#### Publishes

- `/panther/hardware/e_stop` [*std_msgs/Bool*, *latched*]: the current state of the emulated emergency stop.
- `/panther/hardware/motor_on` [*std_msgs/Bool*, *latched*]: informs if motor controllers are on.
- `/panther/hardware/io_state` [*panther_msgs/IOState*, *latched*]: publishes state of panther IO pins. Used for driver compatybility with Panther version 1.06 and below. Message fields with real hardware representation are:
- `motor_on` indicates if motor drivers are powered on.

#### Services advertised

Expand Down
40 changes: 40 additions & 0 deletions panther_power_control/src/power_board_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def __init__(self, name: str) -> None:
io_state.power_button = False
io_state.digital_power = self._read_pin(self._pins.VDIG_OFF)
io_state.charger_enabled = not self._read_pin(self._pins.CHRG_DISABLE)
io_state.motor_on = self._read_pin(self._pins.DRIVER_EN)
self._io_state_pub.publish(io_state)

# -------------------------------
Expand Down Expand Up @@ -120,6 +121,17 @@ def __init__(self, name: str) -> None:
'hardware/e_stop_trigger', Trigger, self._e_stop_trigger_cb
)
self._fan_enable_server = rospy.Service('hardware/fan_enable', SetBool, self._fan_enable_cb)
self._motor_enable_server = rospy.Service(
'hardware/motor_enable', SetBool, self._motor_enable_cb
)

# -------------------------------
# Service clients
# -------------------------------

self._reset_roboteq_script_client = rospy.ServiceProxy(
'driver/reset_roboteq_script', Trigger
)

# -------------------------------
# Timers
Expand Down Expand Up @@ -229,6 +241,34 @@ def _fan_enable_cb(self, req: SetBoolRequest) -> SetBoolResponse:
self._publish_io_state('fan', req.data)
return res

def _motor_enable_cb(self, req: SetBoolRequest) -> SetBoolResponse:
if self._validate_gpio_pin(self._pins.DRIVER_EN, req.data):
return SetBoolResponse(True, f'Motor state already set to: {req.data}')

res = self._set_bool_srv_handle(req.data, self._pins.DRIVER_EN, 'Motor drivers enable')
if not res.success:
return res

self._publish_io_state('motor_on', req.data)

if req.data:
# wait for motor drivers to power on
rospy.sleep(rospy.Duration(2.0))
try:
reset_script_res = self._reset_roboteq_script_client.call()
if not reset_script_res.success:
res = self._set_bool_srv_handle(False, self._pins.DRIVER_EN, 'Motor drivers enable')
if res.success:
self._publish_io_state('motor_on', False)
return SetBoolResponse(reset_script_res.success, reset_script_res.message)
except rospy.ServiceException as e:
res = self._set_bool_srv_handle(False, self._pins.DRIVER_EN, 'Motor drivers enable')
if res.success:
self._publish_io_state('motor_on', False)
return SetBoolResponse(False, f'Failed to reset roboteq script: {e}')

return res

def _set_bool_srv_handle(self, value: bool, pin: int, name: str) -> SetBoolResponse:
rospy.logdebug(f'[{rospy.get_name()}] Requested {name} = {value}')
self._write_to_pin(pin, value)
Expand Down
34 changes: 23 additions & 11 deletions panther_power_control/src/relays_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from std_msgs.msg import Bool
from std_srvs.srv import Trigger, TriggerRequest, TriggerResponse

from panther_msgs.msg import DriverState
from panther_msgs.msg import DriverState, IOState


@dataclass
Expand All @@ -36,8 +36,21 @@ def __init__(self, name: str) -> None:
# Publishers
# -------------------------------

self._motor_on_pub = rospy.Publisher('hardware/motor_on', Bool, queue_size=1, latch=True)
self._e_stop_state_pub = rospy.Publisher('hardware/e_stop', Bool, queue_size=1, latch=True)
self._io_state_pub = rospy.Publisher('hardware/io_state', IOState, queue_size=1, latch=True)

# init e-stop state
self._e_stop_state_pub.publish(self._e_stop_state)

self._io_state = IOState()
self._io_state.motor_on = GPIO.input(self._pins.STAGE2_INPUT)
self._io_state.aux_power = False
self._io_state.charger_connected = False
self._io_state.fan = False
self._io_state.power_button = False
self._io_state.digital_power = True
self._io_state.charger_enabled = False
self._io_state_pub.publish(self._io_state)

# -------------------------------
# Subscribers
Expand All @@ -64,11 +77,9 @@ def __init__(self, name: str) -> None:
# -------------------------------

# check motor state at 10 Hz
self._set_motor_state_timer = rospy.Timer(rospy.Duration(0.1), self._set_motor_state_timer_cb)

# init e-stop state
self._e_stop_state_pub.publish(self._e_stop_state)
self._motor_on_pub.publish(GPIO.input(self._pins.STAGE2_INPUT))
self._set_motor_state_timer = rospy.Timer(
rospy.Duration(0.1), self._set_motor_state_timer_cb
)

rospy.loginfo(f'[{rospy.get_name()}] Node started')

Expand All @@ -89,21 +100,22 @@ def _e_stop_reset_cb(self, req: TriggerRequest) -> TriggerResponse:
False,
'E-STOP reset failed, unable to communicate with motor controllers! Please check connection with motor controllers.',
)

self._e_stop_state = False
self._e_stop_state_pub.publish(self._e_stop_state)
return TriggerResponse(True, 'E-STOP reset successful')

def _e_stop_trigger_cb(self, req: TriggerRequest) -> TriggerResponse:
self._e_stop_state = True
self._e_stop_state_pub.publish(self._e_stop_state)
return TriggerResponse(True, 'E-SROP triggered successful')
return TriggerResponse(True, 'E-STOP triggered successful')

def _set_motor_state_timer_cb(self, *args) -> None:
motor_state = GPIO.input(self._pins.STAGE2_INPUT)
GPIO.output(self._pins.MOTOR_ON, motor_state)
if motor_state != self._motor_on_pub.impl.latch.data:
self._motor_on_pub.publish(motor_state)
if self._io_state.motor_on != motor_state:
self._io_state.motor_on = motor_state
self._io_state_pub.publish(self._io_state)

def _setup_gpio(self) -> None:
GPIO.setwarnings(False)
Expand Down
Loading