-
Notifications
You must be signed in to change notification settings - Fork 1
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
(WIP) Implemented proper ROS service behavior and modified Auto Dig and Auto Offload logic #298
Draft
Isopod00
wants to merge
62
commits into
main
Choose a base branch
from
AutoDig-Improvements
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+140
−227
Draft
Changes from all commits
Commits
Show all changes
62 commits
Select commit
Hold shift + click to select a range
5c98c98
Initial Commit of the Drivetrain Refactor
Isopod00 9382a9c
Merge branch 'main' into Refactor-swerve-into-tank
Isopod00 a976131
refactored read_serial.py to not use absolute encoders per task #275…
kznhq 3ef8cc7
Removed instantiated PID controllers
andrewtomfohrde 2f2f232
Merge branch 'Refactor-swerve-into-tank' of https://github.com/GOFIRS…
andrewtomfohrde 9e64914
deleted all absolute encoders from the arduino code
umnrobotics 846b2fe
Merge branch 'Refactor-swerve-into-tank' of https://github.com/GOFIRS…
umnrobotics 0c1b0b8
refactored drivetrain_node to arcade drive from swerve
ashtondemmer d81054b
Merge branch 'Refactor-swerve-into-tank' of https://github.com/GOFIRS…
ashtondemmer d502c20
Merge branch 'main' into Refactor-swerve-into-tank
Isopod00 b4c1b05
Autoformat the drivetrain_node
Isopod00 d0ea543
Fixed number of bytes to read in read_serial
Isopod00 1cf4c28
Minor Whitespace Fixes
Isopod00 ad4e84c
Fixed the TODO for the Gazebo publishers
Isopod00 6609ef2
Refactor all usages of the drive ROS service
Isopod00 c027945
Merge branch 'main' into Refactor-swerve-into-tank
Isopod00 466a705
Remove remaining mentions of absolute encoders
Isopod00 e984b9a
Merge branch 'main' into Refactor-swerve-into-tank
Isopod00 912db1a
changed swerve joints "type" from continuous to fixed
kznhq c2a1bc4
Fixed Nav2 for tank drive
cparece1 f1e6d55
Updated autodig to do in-place digging
cparece1 62f99ef
Merge branch 'main' into AutoDig-Improvements
Isopod00 3a2056d
small update to autodig to align with new robot design and prepare fo…
cparece1 7a2fb6c
finally commiting this garbage. Scuffed fix for Skimmer node, decent …
cparece1 dfc0719
Fixed the motor_control_node (it got messed up in the previous commit)
Isopod00 bd70e1b
Merge branch 'main' into AutoDig-Improvements
Isopod00 2e93187
Made set_position calls wait to return until position has been reached
cparece1 cb6d18e
Merge branch 'main' into AutoDig-Improvements
Isopod00 a058008
Fixed up set position and set velocity so they dont return til they'r…
cparece1 413be1e
Merge branch 'main' into AutoDig-Improvements
Isopod00 3656b70
Merge branch 'main' into AutoDig-Improvements
Isopod00 1b4fb4a
Removed the async node class, and adapted the digger node, and the A…
cparece1 8b3fa30
Changed logic for extend/retract dumper, reread AutoDig, AutoOff, Dum…
cparece1 dfff57b
TargTach was private so not accessable, added a getter to work around…
cparece1 74f651c
Motor_control_node now builds
cparece1 b0d67cc
Merge branch 'AutoDig-Improvements' of https://github.com/GOFIRST-Rob…
cparece1 297e80e
Merge remote-tracking branch 'origin/main' into AutoDig-Improvements
Isopod00 cc8ee93
Digger_node should be good, motor control has thresholds, but still r…
cparece1 f5e5957
Merge branch 'main' into AutoDig-Improvements
Isopod00 f7c39f7
Auto Formatting
Isopod00 48fe448
velocityThreshold and tachThreshold cleanup
Isopod00 847ba94
Merge branch 'main' into AutoDig-Improvements
Isopod00 c71be3b
Merge branch 'main' into AutoDig-Improvements
Isopod00 b17b67e
Add async await syntax for set_position
jmblixt3 6cf106c
Merge branch 'main' into AutoDig-Improvements
Isopod00 9de17c8
Merge branch 'main' into AutoDig-Improvements
Isopod00 fa0d28e
Merge branch 'main' into AutoDig-Improvements
Isopod00 b52316c
Re-Added Async_sleep via asyncnode to control time spent digging and …
cparece1 3623d28
Added workaround to make dumping berm cancellable (dump is now a seri…
cparece1 b6fd434
Switched method used for raising lift
cparece1 3741a42
dumper and digger now use events for limit switches
cparece1 82c01be
fixed event import and instantiation
cparece1 e05dd76
made stop also set events
cparece1 ed4f5e8
Merge branch 'main' into AutoDig-Improvements
Isopod00 e95ff0f
switched to future from event
cparece1 67c4146
trying smthn new with MultiThreadedExecutor
cparece1 413388a
trying to fix import
cparece1 d1d9f34
fixed running to be lift running
umnrobotics 570e9ce
Digger node with futures to control limit switch behavior
jmblixt3 114bb19
fixed zero callback
cparece1 610e507
Cleaned up
cparece1 d9512aa
Merge branch 'main' into AutoDig-Improvements
Isopod00 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,20 @@ | ||
# This ROS 2 node contains code for the digger subsystem of the robot. | ||
# Original Author: Anthony Brogni <[email protected]> in Fall 2023 | ||
# Maintainer: Anthony Brogni <brogn002@umn.edu> | ||
# Last Updated: November 2023 | ||
# Maintainer: Charlie Parece <parec020@umn.edu> | ||
# Last Updated: October 2024 | ||
|
||
# Import the ROS 2 Python module | ||
from warnings import deprecated | ||
import rclpy | ||
from rclpy.node import Node | ||
|
||
# Import ROS 2 formatted message types | ||
from std_msgs.msg import Bool | ||
from rclpy.executors import MultiThreadedExecutor | ||
|
||
# Import custom ROS 2 interfaces | ||
from rovr_interfaces.srv import MotorCommandSet, MotorCommandGet | ||
from rovr_interfaces.srv import SetPower, SetPosition | ||
from rovr_interfaces.msg import LimitSwitches | ||
from std_srvs.srv import Trigger | ||
|
||
from rclpy.task import Future | ||
|
||
class DiggerNode(Node): | ||
def __init__(self): | ||
|
@@ -34,16 +33,13 @@ def __init__(self): | |
self.srv_lift_stop = self.create_service(Trigger, "lift/stop", self.stop_lift_callback) | ||
self.srv_lift_set_power = self.create_service(SetPower, "lift/setPower", self.lift_set_power_callback) | ||
self.srv_zero_lift = self.create_service(Trigger, "lift/zero", self.zero_lift_callback) | ||
self.srv_lower_lift = self.create_service(Trigger, "lift/lower", self.lower_lift_callback) | ||
|
||
# Define publishers here | ||
self.publisher_goal_reached = self.create_publisher(Bool, "digger/goal_reached", 10) | ||
|
||
# Define subscribers here | ||
self.limit_switch_sub = self.create_subscription(LimitSwitches, "limitSwitches", self.limit_switch_callback, 10) | ||
|
||
# Define timers here | ||
self.timer = self.create_timer(0.1, self.timer_callback) | ||
|
||
# Define default values for our ROS parameters below # | ||
self.declare_parameter("DIGGER_BELT_MOTOR", 2) | ||
self.declare_parameter("DIGGER_LIFT_MOTOR", 1) | ||
|
@@ -60,20 +56,17 @@ def __init__(self): | |
|
||
# Current state of the digger belt | ||
self.running = False | ||
# Current goal position (in degrees) | ||
self.current_goal_position = 0 | ||
# Current position of the lift motor in degrees | ||
self.current_position_degrees = 0 # Relative encoders always initialize to 0 | ||
# Goal Threshold | ||
# if abs(self.current_goal_position - ACTUAL VALUE) <= self.goal_threshold, | ||
# then we should publish True to /digger/goal_reached | ||
self.goal_threshold = 320 # in degrees of the motor # TODO: Tune this threshold if needed | ||
# Current state of the lift system | ||
self.lift_running = False | ||
|
||
# Limit Switch States | ||
self.top_limit_pressed = False | ||
self.bottom_limit_pressed = False | ||
self.top_limit_event = Future() | ||
self.bottom_limit_event = Future() | ||
|
||
|
||
# Maximum value of the lift motor encoder (bottom of the lift system) IN DEGREES | ||
self.MAX_ENCODER_DEGREES = ( | ||
|
@@ -102,44 +95,15 @@ def toggle(self, digger_belt_power: float) -> None: | |
else: | ||
self.set_power(digger_belt_power) | ||
|
||
# TODO: This method can probably be deleted during the implementation of ticket #257 | ||
def set_position(self, position: int) -> None: | ||
"""This method sets the position (in degrees) of the digger.""" | ||
self.current_goal_position = position # goal position should be in degrees | ||
self.cli_motor_set.call_async( | ||
MotorCommandSet.Request( | ||
type="position", | ||
can_id=self.DIGGER_LIFT_MOTOR, | ||
value=float(self.current_goal_position + self.lift_encoder_offset), | ||
) | ||
) | ||
|
||
def stop_lift(self) -> None: | ||
"""This method stops the lift.""" | ||
self.lift_running = False | ||
self.top_limit_event.set_result(False) | ||
self.bottom_limit_event.set_result(False) | ||
self.cli_motor_set.call_async( | ||
MotorCommandSet.Request(type="duty_cycle", can_id=self.DIGGER_LIFT_MOTOR, value=0.0) | ||
) | ||
|
||
def lift_set_power(self, power: float) -> None: | ||
"""This method sets power to the lift system.""" | ||
self.lift_running = True | ||
if power > 0 and self.top_limit_pressed: | ||
self.get_logger().warn("WARNING: Top limit switch pressed!") | ||
self.stop_lift() # Stop the lift system | ||
return | ||
if power < 0 and self.bottom_limit_pressed: | ||
self.get_logger().warn("WARNING: Bottom limit switch pressed!") | ||
self.stop_lift() # Stop the lift system | ||
return | ||
self.cli_motor_set.call_async( | ||
MotorCommandSet.Request(type="duty_cycle", can_id=self.DIGGER_LIFT_MOTOR, value=power) | ||
) | ||
|
||
def zero_lift(self) -> None: | ||
"""This method zeros the lift system by slowly raising it until the top limit switch is pressed.""" | ||
self.lift_set_power(0.05) | ||
|
||
# Define service callback methods here | ||
def set_power_callback(self, request, response): | ||
"""This service request sets power to the digger belt.""" | ||
|
@@ -159,12 +123,17 @@ def toggle_callback(self, request, response): | |
response.success = True | ||
return response | ||
|
||
# TODO: This method needs to be modified during the implementation of ticket #257 | ||
# to return a proper future indicating when the goal position has been reached | ||
# so that rclpy.spin_until_future_complete() can be used to wait for the goal. | ||
def set_position_callback(self, request, response): | ||
"""This service request sets the position of the lift.""" | ||
self.set_position(request.position) | ||
async def set_position_callback(self, request, response): | ||
"""This service request sets the position of the lift. | ||
TODO: Make sure MotorCommandSet.Request(type="position", | ||
is cancellable otherwise this will fail""" | ||
await self.cli_motor_set.call_async( | ||
MotorCommandSet.Request( | ||
type="position", | ||
can_id=self.DIGGER_LIFT_MOTOR, | ||
value=float(request.position + self.lift_encoder_offset), | ||
) | ||
) | ||
response.success = True | ||
return response | ||
|
||
|
@@ -173,51 +142,39 @@ def stop_lift_callback(self, request, response): | |
self.stop_lift() | ||
response.success = True | ||
return response | ||
|
||
|
||
@deprecated | ||
def lift_set_power_callback(self, request, response): | ||
"""This service request sets power to the digger belt.""" | ||
self.lift_set_power(request.power) | ||
response.success = True | ||
return response | ||
|
||
def zero_lift_callback(self, request, response): | ||
async def zero_lift_callback(self, request, response): | ||
"""This service request zeros the lift system.""" | ||
self.zero_lift() | ||
response.success = True | ||
self.lift_set_power(0.05) | ||
self.top_limit_event = Future() | ||
self.top_limit_event.add_done_callback(self.stop_lift) | ||
await self.top_limit_event | ||
return response | ||
|
||
# Define timer callback methods here | ||
def timer_callback(self): | ||
"""Publishes whether or not the current goal position has been reached.""" | ||
# This service call will return a future object, that will eventually contain the position in degrees | ||
future = self.cli_motor_get.call_async(MotorCommandGet.Request(type="position", can_id=self.DIGGER_LIFT_MOTOR)) | ||
future.add_done_callback(self.done_callback) | ||
|
||
def done_callback(self, future): | ||
self.current_position_degrees = future.result().data | ||
goal_reached_msg = Bool( | ||
data=abs(self.current_goal_position + self.lift_encoder_offset - self.current_position_degrees) | ||
<= self.goal_threshold | ||
) | ||
self.publisher_goal_reached.publish(goal_reached_msg) | ||
async def lower_lift_callback(self, request, response): | ||
"""This service request reverse-zeros the lift system, putting it at the lowest point""" | ||
if self.bottom_limit_pressed: | ||
return response | ||
self.lift_set_power(-0.05) | ||
self.bottom_limit_event = Future() | ||
self.bottom_limit_event.add_done_callback(self.stop_lift) | ||
await self.bottom_limit_event | ||
return self.bottom_limit_event.result() | ||
|
||
# Define subscriber callback methods here | ||
def limit_switch_callback(self, limit_switches_msg): | ||
"""This subscriber callback method is called whenever a message is received on the limitSwitches topic.""" | ||
if not self.top_limit_pressed and limit_switches_msg.top_limit_switch: | ||
self.stop_lift() # Stop the lift system | ||
if not self.bottom_limit_pressed and limit_switches_msg.bottom_limit_switch: | ||
self.stop_lift() # Stop the lift system | ||
self.top_limit_pressed = limit_switches_msg.top_limit_switch | ||
self.bottom_limit_pressed = limit_switches_msg.bottom_limit_switch | ||
if self.top_limit_pressed: # If the top limit switch is pressed | ||
self.lift_encoder_offset = self.current_position_degrees | ||
self.get_logger().debug("Current position in degrees: " + str(self.current_position_degrees)) | ||
self.get_logger().debug("New lift encoder offset: " + str(self.lift_encoder_offset)) | ||
elif self.bottom_limit_pressed: # If the bottom limit switch is pressed | ||
self.lift_encoder_offset = self.current_position_degrees - self.MAX_ENCODER_DEGREES | ||
self.get_logger().debug("Current position in degrees: " + str(self.current_position_degrees)) | ||
self.get_logger().debug("New lift encoder offset: " + str(self.lift_encoder_offset)) | ||
if self.top_limit_pressed and not self.top_limit_event.done(): | ||
self.top_limit_pressed = limit_switches_msg.top_limit_switch | ||
self.top_limit_event.set_result(True) | ||
if self.bottom_limit_pressed and not self.top_limit_event.done(): | ||
self.bottom_limit_pressed = limit_switches_msg.bottom_limit_switch | ||
self.bottom_limit_event.set_result(True) | ||
|
||
|
||
def main(args=None): | ||
|
@@ -226,11 +183,15 @@ def main(args=None): | |
|
||
node = DiggerNode() | ||
node.get_logger().info("Initializing the Digger subsystem!") | ||
rclpy.spin(node) | ||
|
||
executor = MultiThreadedExecutor() | ||
executor.add_node(node) | ||
#rclpy.spin(node) | ||
executor.spin() | ||
|
||
executor.shutdown() | ||
node.destroy_node() | ||
rclpy.shutdown() | ||
|
||
|
||
# This code does NOT run if this file is imported as a module | ||
if __name__ == "__main__": | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
#include <string> | ||
#include <tuple> // for tuples | ||
#include <vector> | ||
#include <cmath> | ||
|
||
using namespace std::chrono_literals; | ||
using std::placeholders::_1; | ||
|
@@ -141,6 +142,9 @@ class PIDController { | |
bool isContinuousInputEnabled() { | ||
return this->continuous; | ||
} | ||
int32_t getTargTach() { | ||
return this->targTach; | ||
} | ||
}; | ||
|
||
class MotorControlNode : public rclcpp::Node { | ||
|
@@ -221,9 +225,13 @@ class MotorControlNode : public rclcpp::Node { | |
this->pid_controllers[id]->isActive = false; | ||
} | ||
int32_t data = rpm; | ||
|
||
send_can(id + 0x00000300, data); // ID must be modified to signify this is a RPM command | ||
this->current_msg[id] = std::make_tuple(id + 0x00000300, data); // update the hashmap | ||
while(abs(rpm-this->can_data[id].velocity - rpm) > this->velocityThreshold) | ||
{ | ||
continue; | ||
} | ||
RCLCPP_DEBUG(this->get_logger(), "Setting the velocity of CAN ID: %u to %d RPM", id, rpm); // Print Statement | ||
} | ||
|
||
|
@@ -232,6 +240,10 @@ class MotorControlNode : public rclcpp::Node { | |
if (this->pid_controllers[id]) { | ||
this->pid_controllers[id]->setRotation(position); | ||
} | ||
while(abs(this->can_data[id].tachometer - this->pid_controllers[id]->getTargTach()) > this->tachThreshold) | ||
{ | ||
continue; | ||
} | ||
Comment on lines
+243
to
+246
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you do if you want to stop the motor before it reaches its final position |
||
RCLCPP_DEBUG(this->get_logger(), "Setting the position of CAN ID: %u to %d degrees", id, position); // Print Statement | ||
} | ||
|
||
|
@@ -303,6 +315,9 @@ class MotorControlNode : public rclcpp::Node { | |
} | ||
} | ||
|
||
int velocityThreshold = 50; // TODO: Tune this value | ||
int tachThreshold = 100; // TODO: Tune this value | ||
|
||
// Listen for CAN status frames sent by our VESC motor controllers | ||
void CAN_callback(const can_msgs::msg::Frame::SharedPtr can_msg) { | ||
uint32_t motorId = can_msg->id & 0xFF; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again waiting in while loops like this is quite cpu intensive, try something event driven