Skip to content

Commit

Permalink
add movm (move map) action
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulBouchier committed Nov 13, 2024
1 parent b034b7b commit 8c8adf8
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 6 deletions.
7 changes: 7 additions & 0 deletions launch/servers_launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ def generate_launch_description():
output='screen',
emulate_tty=True
),
Node(
package='scripted_bot_driver',
executable='drive_straight_map',
name='drive_straight_map',
output='screen',
emulate_tty=True
),
Node(
package='scripted_bot_driver',
executable='rotate_odom',
Expand Down
114 changes: 114 additions & 0 deletions scripted_bot_driver/drive_straight_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env python

import sys
import time
from math import pow, sqrt

import rclpy
from rclpy.executors import MultiThreadedExecutor

from scripted_bot_driver.move_parent import MoveParent
from scripted_bot_interfaces.msg import DriveStraightDebug


class DriveStraightMap(MoveParent):
def __init__(self):
super().__init__('drive_straight_map')

# Publisher for debug data
self.debug_msg = DriveStraightDebug()
self.debug_pub = self.create_publisher(DriveStraightDebug, 'drive_straight_debug', 10)

# create the action server for this move-type
self.create_action_server('drive_straight_map')

def parse_argv(self, argv):
self.get_logger().info('parsing move_spec {}'.format(argv))
self.run_once = True
self.delta_map = 0.0
self.set_defaults()

if (len(argv) != 1 and len(argv) != 2):
self.get_logger().fatal('Incorrrect number of args given to DriveStraightMap: {}'.format(len(argv)))
return -1

try:
self.distance = float(argv[0]) # pick off first arg from supplied list
self.debug_msg.distance = self.distance
if self.distance < 0:
self.distance = -self.distance # distance is always +ve
self.speed = -self.speed # go backward

# a supplied speed argument overrides everything
if len(argv) == 2:
self.speed = float(argv[1])
self.get_logger().info('Using supplied speed {}'.format(self.speed))
except ValueError:
self.get_logger().error('Invalid argument given: {}'.format(argv))
return -1
return len(argv) # return number of args consumed


# run is called at the rate until it returns true. It does not stop motion on
# completion - caller is responsible for stopping motion.
def run(self):
if not self.is_map_started():
self.get_logger().error('ERROR: robot map topic has not started - exiting')
return True

if self.run_once:
self.initial_x = self.map.pose.position.x
self.initial_y = self.map.pose.position.y
self.get_logger().info('Drive straight using map for {} m at speed: {}'.format(self.distance, self.speed))
self.get_logger().info('Initial X-Y: {} {}, goal distance: {}'.format(self.initial_x, self.initial_y, self.distance))
self.debug_msg.initial_x = self.initial_x
self.debug_msg.initial_y = self.initial_y
self.run_once = False

delta_x = self.map.pose.position.x - self.initial_x
delta_y = self.map.pose.position.y - self.initial_y
self.delta_map = sqrt(pow(delta_x, 2) + pow(delta_y, 2))
# self.get_logger().info('delta_x: {} delta_y: {} delta_map: {}'.format(delta_x, delta_y, self.delta_map))

if (self.delta_map > self.distance):
self.send_move_cmd(0.0, 0.0) # traveled required distance, slam on the brakes
self.get_logger().info('traveled: {} m'.format(self.delta_map))
return True

# accelerate to full speed as long as we haven't reached the goal
self.send_move_cmd(self.slew_vel(self.speed), self.slew_rot(0.0))

# publish debug data
self.debug_msg.delta_odom = self.delta_map
self.debug_msg.speed = self.speed
self.debug_msg.commanded_linear = self.commandedLinear
self.debug_msg.commanded_angular = self.commandedAngular
self.debug_pub.publish(self.debug_msg)

return False

def get_feedback(self):
progress_feedback = self.delta_map
text_feedback = 'Driving straight at {}, traveled {}m'.format(
self.commandedLinear, progress_feedback)
return text_feedback, progress_feedback

# finished action, clean up after a move and reset defaults for next move
def finish_cb(self):
self.set_defaults()
results = [self.delta_map]
return results

def main():
rclpy.init()
nh = DriveStraightMap()

# Use a MultiThreadedExecutor to enable processing goals concurrently
executor = MultiThreadedExecutor()
rclpy.spin(nh, executor=executor)

nh.destroy_node()
rclpy.shutdown()

if __name__ == '__main__':
main()
16 changes: 14 additions & 2 deletions scripted_bot_driver/move_parent.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from rclpy.node import Node
from rclpy.action import ActionServer, CancelResponse, GoalResponse

from geometry_msgs.msg import Twist
from geometry_msgs.msg import Twist, PoseStamped
from nav_msgs.msg import Odometry
from scripted_bot_interfaces.action import Move
from rcl_interfaces.msg import ParameterDescriptor
Expand Down Expand Up @@ -55,8 +55,13 @@ def __init__(self, node_name):
# subscribers to robot data
self.odom = Odometry()
self.odom_started = False
self.subscription = self.create_subscription(
self.odom_sub = self.create_subscription(
Odometry, 'odom', self.odom_callback, 10)

self.map = PoseStamped()
self.map_started = False
self.map_sub = self.create_subscription(
PoseStamped, 'map', self.map_callback, 10)

# Publisher to control robot motion
self.move_cmd = Twist()
Expand Down Expand Up @@ -95,6 +100,13 @@ def odom_callback(self, odom_msg):
def is_odom_started(self):
return self.odom_started

def map_callback(self, map_msg):
self.map = map_msg
self.map_started = True

def is_map_started(self):
return self.map_started

def set_defaults(self):
# linear speed parameters
self.speed = self.get_parameter('speed_default_param').get_parameter_value().double_value
Expand Down
15 changes: 11 additions & 4 deletions scripted_bot_driver/scripted_mover.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __init__(self):
super().__init__('scripted_mover_client')
self.stop_client = ActionClient(self, Move, 'stop')
self.drive_straight_client = ActionClient(self, Move, 'drive_straight_odom')
self.drive_straight_map_client = ActionClient(self, Move, 'drive_straight_map')
self.rotate_odom_client = ActionClient(self, Move, 'rotate_odom')
self.drive_waypoints_client = ActionClient(self, Move, 'drive_waypoints')
self.seek2cone_client = ActionClient(self, Move, 'seek2cone')
Expand All @@ -32,15 +33,17 @@ def parse_moves(self, argv):
current_arg = 0
self.single_moves = []
self.current_single_move = 0
supported_moves = ['stop', 'movo', 'roto', 'drive_waypoints', 'seek2cone']
supported_moves = ['stop', 'movo', 'movm', 'roto', 'drive_waypoints', 'seek2cone']

print('argv: {}'.format(argv))
while current_arg != len(argv):
# extract the move_type from argv
if argv[current_arg] == 'stop':
move_type = 'stop'
elif argv[current_arg] == 'movo':
move_type = 'drive_straight'
move_type = 'drive_straight_odom'
elif argv[current_arg] == 'movm':
move_type = 'drive_straight_map'
elif argv[current_arg] == 'roto':
move_type = 'rotate'
elif argv[current_arg] == 'drive_waypoints':
Expand Down Expand Up @@ -77,9 +80,12 @@ def run_single_move(self):
if(single_move.move_type == 'stop'):
self.send_goal(self.stop_client, single_move.move_spec)
self.get_logger().info('sent stop goal')
elif(single_move.move_type == 'drive_straight'):
elif(single_move.move_type == 'drive_straight_odom'):
self.send_goal(self.drive_straight_client, single_move.move_spec)
self.get_logger().info('sent drive_straight goal')
elif(single_move.move_type == 'drive_straight_map'):
self.send_goal(self.drive_straight_map_client, single_move.move_spec)
self.get_logger().info('sent drive_straight_map goal')
elif(single_move.move_type == 'rotate'):
self.send_goal(self.rotate_odom_client, single_move.move_spec)
self.get_logger().info('sent rotate goal')
Expand Down Expand Up @@ -135,7 +141,8 @@ def get_result_callback(self, future):
def usage():
print('Usage: scripted_mover.py [commands] - executes the series of move commands provided')
print('Supported move commands are:')
print('movo <distance> [speed] - drive straight for <distance> meters')
print('movo <distance> [speed] - drive straight for <distance> meters using odometry')
print('movm <distance> [speed] - drive straight for <distance> meters using map nav')
print('seek2cone <max_distance> [speed] - seek cone for <max_distance> meters')
print('roto <target_angle>[d|p] <mode> [angular_speed][d] [drive_speed] - rotate <angle> radians, or pi*<target_angle> if [p] or degrees if [d], +angle is CCW., mode 1 (default) is shortest_path, mode 2 is strict. Angular speed, if given, allows same modifiers as target_angle. Drive speed if given is in meters per second, defaults to zero.')
print('drive_waypoints <target_x> <target_y> [ more_targets ] - drive to a list of targets')
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
entry_points={
'console_scripts': [
'drive_straight_odom = scripted_bot_driver.drive_straight_odom:main',
'drive_straight_map = scripted_bot_driver.drive_straight_map:main',
'drive_waypoints = scripted_bot_driver.drive_waypoints:main',
'seek2cone = scripted_bot_driver.seek2cone:main',
'stop = scripted_bot_driver.stop:main',
Expand Down

0 comments on commit 8c8adf8

Please sign in to comment.