diff --git a/franka_logistics/grasp_demo_node.py b/franka_logistics/grasp_demo_node.py
new file mode 100644
index 0000000..4726150
--- /dev/null
+++ b/franka_logistics/grasp_demo_node.py
@@ -0,0 +1,249 @@
+import rclpy
+from rclpy.node import Node
+from rclpy.action import ActionClient, ActionServer
+from rclpy.duration import Duration
+
+from franka_msgs.action import Grasp
+from franka_msgs.action import Move
+from control_msgs.action import FollowJointTrajectory
+from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
+from franka_cps_msgs.action import GraspSequence
+
+from threading import Event
+
+import sqlite3
+
+class GraspDemoNode(Node):
+ def __init__(self):
+ super().__init__('grasp_demo_node')
+
+ # Event Handling
+ self.action_completed_event = Event()
+ self._action_result = None
+ # Grasp Sequence Service
+ self.execute_action = ActionServer(self, GraspSequence,
+ 'franka_grasp/grasp_sequence', self.execute_callback)
+
+ # Franka Position Controller
+ self.position_client = ActionClient(self, FollowJointTrajectory,
+ 'panda_arm_controller/follow_joint_trajectory')
+
+ self._send_position_future = None
+ self._position_result_future = None
+
+ # Franka Gripper Action Server clients for Move and Grasp
+ self.franka_move_action_client = ActionClient(self, Move, 'panda_gripper/move')
+ self.franka_grasp_action_client = ActionClient(self, Grasp, 'panda_gripper/grasp')
+
+ self._send_move_future = None
+ self._move_result_future = None
+ self._send_grasp_future = None
+ self._grasp_result_future = None
+
+ # Sqlite Database
+ self.conn = None
+ self.cursor = None
+ self.database_name = '/home/niko/sqlite_dbs/logistics_db'
+
+ self.init_db()
+
+ def init_db(self):
+ # Connect to the sqlite db
+ self.conn = sqlite3.connect(self.database_name)
+ self.cursor = self.conn.cursor()
+
+ self.cursor.execute(
+ '''CREATE TABLE IF NOT EXISTS position_table (
+ position_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ position_name TEXT UNIQUE NOT NULL,
+ joint_1 REAL NOT NULL,
+ joint_2 REAL NOT NULL,
+ joint_3 REAL NOT NULL,
+ joint_4 REAL NOT NULL,
+ joint_5 REAL NOT NULL,
+ joint_6 REAL NOT NULL,
+ joint_7 REAL NOT NULL)''')
+
+ self.cursor.execute('''
+ CREATE TABLE IF NOT EXISTS position_sequence_table (
+ sequence_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ sequence_name TEXT UNIQUE NOT NULL,
+ position_idle TEXT NOT NULL,
+ position_hower TEXT NOT NULL,
+ position_grasp TEXT NOT NULL,
+ position_drop TEXT NOT NULL,
+ FOREIGN KEY (position_idle) REFERENCES position_table (position_name),
+ FOREIGN KEY (position_hower) REFERENCES position_table (position_name),
+ FOREIGN KEY (position_grasp) REFERENCES position_table (position_name),
+ FOREIGN KEY (position_drop) REFERENCES position_table (position_name)
+ )
+ ''')
+ self.conn.commit()
+ self.get_logger().info('Initializing Grasp Demo Node finished!')
+
+ def execute_callback(self, goal_handle):
+ self.get_logger().info(' Executing Grasp Sequence')
+ feedback = GraspSequence.Feedback()
+
+ sequence = [
+ (self.arm_move_to_position, (goal_handle.request.hower, 10), "Moving to Hower Position"),
+ (self.arm_move_to_position, (goal_handle.request.grasp, 10), "Moving to Grasp Position"),
+ (self.gripper_grasp, None, "Grasping Object"),
+ (self.arm_move_to_position, (goal_handle.request.hower, 10), "Moving back to Hower Position"),
+ (self.arm_move_to_position, (goal_handle.request.drop, 10), "Moving back to Drop Position"),
+ (self.gripper_move, 0.08, "Opening Gripper"),
+ (self.arm_move_to_position, (goal_handle.request.idle, 5), "Moving to Idle Position")
+ ]
+
+ for action, args, step_descrioption in sequence:
+ feedback.current_step = step_descrioption
+ goal_handle.publish_feedback(feedback)
+ # if goal_handle.is_cancle_requested():
+ # goal_handle.canceled()
+ # return GraspSequence.Result(success=False, error_code="Canceled")
+
+ success = action(*args) if args else action()
+ if not success:
+ goal_handle.abort()
+ return GraspSequence.Result(success=False, error_code="aborted")
+
+ goal_handle.succeed()
+ return GraspSequence.Result(sucess=True)
+
+
+ # region Position Controller
+ def arm_move_to_position(self, joint_positions, duration):
+ self.action_completed_event.clear() # Reset the event before sending a new goal
+
+ goal_msg = FollowJointTrajectory.Goal()
+ trajectory = JointTrajectory()
+ trajectory.joint_names = ['panda_joint1', 'panda_joint2',
+ 'panda_joint3', 'panda_joint4',
+ 'panda_joint5', 'panda_joint6',
+ 'panda_joint7']
+ point = JointTrajectoryPoint()
+ point.positions = joint_positions.position
+ time = Duration(seconds=duration)
+ self.get_logger().error(f"Time: {time}")
+ point.time_from_start = time.to_msg()
+ self.get_logger().error(f"NEW Point{point}")
+ trajectory.points.append(point)
+ goal_msg.trajectory = trajectory
+ self.get_logger().error(f'Goal:{goal_msg}')
+
+ self.position_client.wait_for_server()
+ self._send_position_future = self.position_client.send_goal_async(goal_msg)
+ self._send_position_future.add_done_callback(self.position_response_callback)
+ # Wait here until the action completes
+ self.action_completed_event.wait()
+
+ # After the event is set in position_result_callback
+ if hasattr(self, '_action_result') and self._action_result:
+ self._action_result = None
+ return True
+ return False
+
+ def position_response_callback(self, future):
+ goal_handle = future.result()
+ if not goal_handle.accepted:
+ self.get_logger().info('Positon goal rejected!')
+ self.action_completed_event.set() # Signal that the action is completed (with failure)
+ return
+
+ self.get_logger().info('Position goal accepted!')
+ self._position_result_future = goal_handle.get_result_async()
+ self._position_result_future.add_done_callback(self.position_result_callback)
+
+ def position_result_callback(self, future):
+ result = future.result().result
+ self.get_logger().info(f'Position result: Success: {result.success}, Error: {result.error}')
+ self._action_result = result.success # Store the result for checking after wait
+ self.action_completed_event.set() # Signal that the action is completed
+ # endregion
+
+ # region Grasp Action
+ def gripper_grasp(self):
+ self.action_completed_event.clear() # Reset the event before sending a new goal
+
+ goal_msg = Grasp()
+ goal_msg.width = 0.0
+ goal_msg.speed = 0.1
+ goal_msg.force = 50.0
+
+ self.franka_grasp_action_client.wait_for_server()
+ self._send_grasp_future = self.franka_grasp_action_client.send_goal_async(goal_msg, feedback_callback=self.action_fb_callback)
+ self._send_grasp_future.add_done_callback(self.grasp_response_callback)
+ self.action_completed_event.wait()
+
+ # After the event is set in grasp_result_callback
+ if hasattr(self, '_action_result') and self._action_result:
+ self._action_result = None
+ return True
+ return False
+
+ def action_fb_callback(self, msg):
+ feedback = msg.feedback
+ self.get_logger().info(f'Received feedback: Current Width: {feedback.current_width}')
+
+ def grasp_response_callback(self, future):
+ goal_handle = future.result()
+ if not goal_handle.accepted:
+ self.get_logger().info('Grasp goal rejected!')
+ self.action_completed_event.set() # Signal that the action is completed (with failure)
+ return
+
+ self.get_logger().info('Grasp goal accepted!')
+ self._grasp_result_future = goal_handle.get_result_async()
+ self._grasp_result_future.add_done_callback(self.grasp_result_callback)
+
+ def grasp_result_callback(self, future):
+ result = future.result().result
+ self.get_logger().info(f'Grasp result: Success: {result.success}, Error: {result.error}')
+ self._action_result = result.success # Store the result for checking after wait
+ self.action_completed_event.set() # Signal that the action is completed
+ # endregion
+
+ # region Move Action
+ def gripper_move(self, width):
+ goal_msg = Move()
+ goal_msg.width = width
+ goal_msg.speed = 0.1
+
+ self.franka_move_action_client.wait_for_server()
+ self._send_move_future = self.franka_move_action_client.send_goal_async(goal_msg, feedback_callback=self.action_fb_callback)
+ self._send_move_future.add_done_callback(self.move_response_callback)
+ self.action_completed_event.wait()
+
+ # After the event is set in move_result_callback
+ if hasattr(self, '_action_result') and self._action_result:
+ self._action_result = None
+ return True
+ return False
+
+ def move_response_callback(self, future):
+ goal_handle = future.result()
+ if not goal_handle.accepted:
+ self.get_logger().info('Move goal rejected!')
+ self.action_completed_event.set() # Signal that the action is completed (with failure)
+ return
+
+ self.get_logger().info('Move goal accepted!')
+ self._move_result_future = goal_handle.get_result_async()
+ self._move_result_future.add_done_callback(self.move_result_callback)
+
+ def move_result_callback(self, future):
+ result = future.result().result
+ self.get_logger().info(f'Move result: Success: {result.success}, Error: {result.error}')
+ self._action_result = result.success # Store the result for checking after wait
+ self.action_completed_event.set() # Signal that the action is completed
+ # endregion
+
+
+def main(args=None):
+ rclpy.init(args=args)
+ node = GraspDemoNode()
+ rclpy.spin(node)
+ rclpy.shutdown()
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/franka_logistics/position_learn_gui.py b/franka_logistics/position_learn_gui.py
new file mode 100644
index 0000000..f751c6c
--- /dev/null
+++ b/franka_logistics/position_learn_gui.py
@@ -0,0 +1,489 @@
+import threading
+import tkinter as tk
+from tkinter import ttk
+
+import sqlite3
+
+import rclpy
+from rclpy.node import Node
+
+from sensor_msgs.msg import JointState
+
+
+class GUINode(Node):
+ def __init__(self):
+ super().__init__('position_learn_gui_node')
+
+ self.create_subscription(JointState, 'franka/joint_states', self.position_callback, 10)
+
+ self.current_position = {
+ 'joint_1': 0.0,
+ 'joint_2': 0.0,
+ 'joint_3': 0.0,
+ 'joint_4': 0.0,
+ 'joint_5': 0.0,
+ 'joint_6': 0.0,
+ 'joint_7': 0.0
+ }
+
+ def position_callback(self, msg):
+ self.current_position['joint_1'] = msg.position[0]
+ self.current_position['joint_2'] = msg.position[1]
+ self.current_position['joint_3'] = msg.position[2]
+ self.current_position['joint_4'] = msg.position[3]
+ self.current_position['joint_5'] = msg.position[4]
+ self.current_position['joint_6'] = msg.position[5]
+ self.current_position['joint_7'] = msg.position[6]
+
+
+class MyGUI:
+ def __init__(self, root, node):
+ self.root = root
+ self.root.title("Franka Grasp Interface")
+
+ # ROS2 Node
+ self.node = node
+
+ # Connect to your SQLite database
+ self.conn = sqlite3.connect('/home/niko/sqlite_dbs/logistics_db')
+ self.cursor = self.conn.cursor()
+
+ # Track the current mode
+ self.current_mode = 'Learn' # Starts in Learn mode
+
+ # Initialize frames for Learn and Run modes
+ self.learn_frame = ttk.Frame(root)
+
+ # Position Submenu attributes
+ self.position_name = ''
+ self.position_entries = {}
+ self.position_entries_values = {
+ 'joint_1': 0.0,
+ 'joint_2': 0.0,
+ 'joint_3': 0.0,
+ 'joint_4': 0.0,
+ 'joint_5': 0.0,
+ 'joint_6': 0.0,
+ 'joint_7': 0.0
+ }
+
+ # Grasp Submenu attributes
+ self.grasp_name = ''
+ self.idle = ''
+ self.hower = ''
+ self.grasp = ''
+ self.drop = ''
+
+ # Database Submenu attributes
+ self.position_db = ''
+ self.grasp_db = ''
+
+ # Grasp Run Submenu attributes
+ self.grasp_run = ''
+ self.grasp_entries = {}
+
+ # GUI Subcomponents
+ self.position_name_entry = None
+
+ self.grasp_name_entry = None
+ self.idle_selector = None
+ self.hower_selector = None
+ self.grasp_selector = None
+ self.drop_selector = None
+ self.width_grasp_entry = None
+ self.width_idle_entry = None
+
+ self.position_db_selector = None
+ self.grasp_db_selector = None
+
+ # Frames
+ self.position_frame = None
+ self.grasp_frame = None
+ self.db_frame = None
+ self.separator_1 = None
+ self.separator_2 = None
+
+ # Initially set up Learn mode
+ self.setup_learn_mode()
+
+ #
+
+ def setup_learn_mode(self):
+ self.create_position_column()
+ self.separator_1 = tk.Canvas(self.root, width=1, bg="grey")
+ self.separator_1.pack(side=tk.LEFT, fill=tk.Y)
+ self.create_grasp_column()
+ self.separator_2 = tk.Canvas(self.root, width=1, bg="grey")
+ self.separator_2.pack(side=tk.LEFT, fill=tk.Y)
+ self.create_db_edit_column()
+
+ #
+
+ #
+ def set_initial_position_values(self):
+ initial_values = {
+ 'joint_1': 0.0,
+ 'joint_2': 0.0,
+ 'joint_3': 0.0,
+ 'joint_4': 0.0,
+ 'joint_5': 0.0,
+ 'joint_6': 0.0,
+ 'joint_7': 0.0
+ }
+ for key, value in initial_values.items():
+ if key in self.position_entries:
+ self.position_entries[key].config(state='normal') # Enable writing
+ self.position_entries[key].delete(0, tk.END)
+ self.position_entries[key].insert(0, str(value))
+ self.position_entries[key].config(state='readonly') # Set back to read-only
+
+ #
+
+ #
+ def get_position_names(self):
+ self.cursor.execute("SELECT position_name FROM position_table")
+ return [row[0] for row in self.cursor.fetchall()]
+
+ def get_grasp_names(self):
+ self.cursor.execute("SELECT sequence_name FROM position_sequence_table")
+ return [row[0] for row in self.cursor.fetchall()]
+
+ def get_position_by_name(self, position_name):
+ self.cursor.execute(
+ "SELECT joint_1, joint_2, joint_3, joint_4, joint_5, joint_6, joint_7 FROM position_table WHERE position_name = ?",
+ (position_name,))
+ result = self.cursor.fetchone()
+ if result:
+ position = JointState()
+ position.position = result
+ return position
+ else:
+ print("Position not found")
+ return None
+
+ def get_grasp_sequence_by_name(self, sequence_name):
+ self.cursor.execute(
+ "SELECT position_idle, position_hower, position_grasp, position_drop"
+ " FROM position_sequence_table WHERE sequence_name = ?",
+ (sequence_name,))
+ result = self.cursor.fetchone()
+ if result:
+ self.grasp_entries['idle_position'] = result[0]
+ self.grasp_entries['hower_position'] = result[1]
+ self.grasp_entries['grasp_position'] = result[2]
+ self.grasp_entries['drop_position'] = result[3]
+ else:
+ print("Sequence not found")
+
+ def add_new_position(self, position_name, joint_1, joint_2, joint_3, joint_4, joint_5, joint_6, joint_7):
+ self.cursor.execute(
+ "INSERT INTO position_table (position_name, joint_1, joint_2, joint_3, joint_4, joint_5, joint_6, joint_7)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
+ (position_name,
+ joint_1,
+ joint_2,
+ joint_3,
+ joint_4,
+ joint_5,
+ joint_6,
+ joint_7))
+
+ self.conn.commit()
+
+ def add_new_grasp(self, sequence_name, idle, hower, grasp, drop):
+ print(sequence_name, idle, hower, grasp, drop)
+ self.cursor.execute("INSERT INTO position_sequence_table"
+ """ (sequence_name, position_idle, position_hower, position_grasp, position_drop)
+ VALUES (?, ?, ?, ?, ?)""",
+ (sequence_name, idle, hower, grasp, drop))
+
+ self.conn.commit()
+
+ def delete_position_by_name(self, position_name):
+ self.cursor.execute("DELETE FROM position_table WHERE position_name = ?", (position_name,))
+
+ self.conn.commit()
+
+ def delete_grasp_by_name(self, sequence_name):
+ self.cursor.execute("DELETE FROM position_sequence_table WHERE sequence_name = ?", (sequence_name,))
+
+ self.conn.commit()
+
+ #
+
+ #
+ def update_position_values(self):
+ for key, value in self.node.current_position.items():
+ if key in self.position_entries:
+ self.position_entries[key].config(state='normal') # Enable writing
+ self.position_entries[key].delete(0, tk.END)
+ self.position_entries[key].insert(0, f"{value:.5f}")
+ self.position_entries[key].config(state='readonly') # Set back to read-only
+
+ def update_position_selectors(self, db_positions):
+ self.idle_selector['values'] = db_positions
+ self.hower_selector['values'] = db_positions
+ self.grasp_selector['values'] = db_positions
+ self.drop_selector['values'] = db_positions
+
+ def update_db_selectors(self, db_positions, db_grasp):
+ self.position_db_selector['values'] = db_positions
+ self.grasp_db_selector['values'] = db_grasp
+
+ def update_from_database(self):
+ db_positions = self.get_position_names()
+ db_grasps = self.get_grasp_names()
+ self.update_position_selectors(db_positions)
+ self.update_db_selectors(db_positions, db_grasps)
+
+ #
+
+ #
+ # Position Events
+ def position_set_event(self):
+ self.position_name = self.position_name_entry.get()
+
+ self.add_new_position(self.position_name,
+ float(self.position_entries['joint_1'].get()),
+ float(self.position_entries['joint_2'].get()),
+ float(self.position_entries['joint_3'].get()),
+ float(self.position_entries['joint_4'].get()),
+ float(self.position_entries['joint_5'].get()),
+ float(self.position_entries['joint_6'].get()),
+ float(self.position_entries['joint_7'].get()))
+ print("Position Button Clicked! Position Name:", self.position_name)
+
+ # Grasp Events
+ def idle_selector_event(self, event):
+ self.idle = self.idle_selector.get()
+
+ def hower_selector_event(self, event):
+ self.hower = self.hower_selector.get()
+
+ def grasp_selector_event(self, event):
+ self.grasp = self.grasp_selector.get()
+
+ def drop_selector_event(self, event):
+ self.drop = self.drop_selector.get()
+
+ def grasp_set_event(self):
+ self.grasp_name = self.grasp_name_entry.get()
+ self.add_new_grasp(self.grasp_name,
+ self.idle,
+ self.hower,
+ self.grasp,
+ self.drop)
+ print("Grasp Button Clicked! Grasp Name:", self.grasp_name)
+
+ # Database Events
+ def position_db_selector_event(self, event):
+ self.position_db = self.position_db_selector.get()
+
+ def grasp_db_selector_event(self, event):
+ self.grasp_db = self.grasp_db_selector.get()
+
+ def position_delete_event(self):
+ self.delete_position_by_name(self.position_db)
+ print("Position: ", self.position_db, ' was deleted!')
+
+ def grasp_delete_event(self):
+ self.delete_grasp_by_name(self.grasp_db)
+ print("Grasp: ", self.grasp_db, ' was deleted!')
+
+ # Run Events
+ def grasp_run_selector_event(self, event):
+ self.grasp_run = self.grasp_run_selector.get()
+ self.get_grasp_sequence_by_name(self.grasp_run)
+
+ def grasp_perform_event(self):
+ idle = self.get_position_by_name(self.idle)
+ hower = self.get_position_by_name(self.hower)
+ grasp = self.get_position_by_name(self.grasp)
+ drop = self.get_position_by_name(self.drop)
+
+ feedback = self.node.send_grasp_sequence_goal(idle, hower, grasp, drop)
+
+ print("Grasp: ", self.grasp_run, ' is performed!')
+
+ #
+
+ #
+ def validate_float(self, P):
+ try:
+ value = float(P)
+ if self.width_lower <= value <= self.width_upper:
+ return True
+ else:
+ return False
+ except ValueError:
+ return False
+
+ #
+
+ #
+ def create_position_column(self):
+ self.position_frame = tk.Frame(self.root)
+ self.position_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ submenu_label = tk.Label(self.position_frame, text="New Position", font=("Arial", 12, "bold"))
+ submenu_label.grid(row=0, column=0, columnspan=2)
+
+ # Name Entry
+ tk.Label(self.position_frame, text="Position Name").grid(row=1, column=0)
+ self.position_name_entry = tk.Entry(self.position_frame)
+ self.position_name_entry.grid(row=1, column=1)
+
+ # Separator
+ separator = ttk.Separator(self.position_frame, orient='horizontal')
+ separator.grid(row=2, column=0, columnspan=2, sticky='ew')
+
+ # Title for Current Position
+ current_position_label = tk.Label(self.position_frame, text="Current Position", font=("Arial", 10, "bold"))
+ current_position_label.grid(row=3, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.position_frame, orient='horizontal')
+ separator.grid(row=4, column=0, columnspan=2, sticky='ew')
+
+ # Position Values Entries
+ position_labels = ['Joint 1', 'Joint 2', 'Joint 3', 'Joint 4', 'Joint 5', 'Joint 6', 'Joint 7']
+ for i, label in enumerate(position_labels, start=5):
+ tk.Label(self.position_frame, text=label).grid(row=i, column=0)
+ entry = tk.Entry(self.position_frame, state='readonly', width=10)
+ entry.grid(row=i, column=1)
+ self.position_entries[label.replace(' ', '_').lower()] = entry
+
+ self.set_initial_position_values()
+
+ # Separator
+ separator = ttk.Separator(self.position_frame, orient='horizontal')
+ separator.grid(row=14, column=0, columnspan=2, sticky='ew')
+
+ # Set Button
+ set_button = tk.Button(self.position_frame, text="Set New Position", command=self.position_set_event)
+ set_button.grid(row=15, column=0, columnspan=2)
+
+ self.position_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ def create_grasp_column(self):
+ self.grasp_frame = tk.Frame(self.root)
+ self.grasp_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ submenu_label = tk.Label(self.grasp_frame, text="New Grasp", font=("Arial", 12, "bold"))
+ submenu_label.grid(row=0, column=0, columnspan=2)
+
+ # Name Entry
+ tk.Label(self.grasp_frame, text="Grasp Name").grid(row=1, column=0)
+ self.grasp_name_entry = tk.Entry(self.grasp_frame)
+ self.grasp_name_entry.grid(row=1, column=1)
+
+ # Separator
+ separator = ttk.Separator(self.grasp_frame, orient='horizontal')
+ separator.grid(row=2, column=0, columnspan=2, sticky='ew')
+
+ # Title for Current Position
+ current_position_label = tk.Label(self.grasp_frame, text="Grasp Position", font=("Arial", 10, "bold"))
+ current_position_label.grid(row=3, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.grasp_frame, orient='horizontal')
+ separator.grid(row=4, column=0, columnspan=2, sticky='ew')
+
+ # Idle Position Selector
+ idle_label = tk.Label(self.grasp_frame, text="Idle Position", font="Arial")
+ idle_label.grid(row=5, column=0)
+ self.idle_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.idle_selector.grid(row=5, column=1)
+ self.idle_selector.bind('<>', self.idle_selector_event)
+
+ # Hower Position Selector
+ hower_label = tk.Label(self.grasp_frame, text="Hower Position", font="Arial")
+ hower_label.grid(row=6, column=0)
+ self.hower_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.hower_selector.grid(row=6, column=1)
+ self.hower_selector.bind('<>', self.hower_selector_event)
+
+ # Grasp Position Selector
+ grasp_label = tk.Label(self.grasp_frame, text="Grasp Position", font="Arial")
+ grasp_label.grid(row=7, column=0)
+ self.grasp_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.grasp_selector.grid(row=7, column=1)
+ self.grasp_selector.bind('<>', self.grasp_selector_event)
+
+ # Drop Position Selector
+ drop_label = tk.Label(self.grasp_frame, text="Drop Position", font="Arial")
+ drop_label.grid(row=8, column=0)
+ self.drop_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.drop_selector.grid(row=8, column=1)
+ self.drop_selector.bind('<>', self.drop_selector_event)
+
+ # Separator
+ separator = ttk.Separator(self.grasp_frame, orient='horizontal')
+ separator.grid(row=12, column=0, columnspan=2, sticky='ew')
+
+ # Set Button
+ set_button = tk.Button(self.grasp_frame, text="Set New Grasp", command=self.grasp_set_event)
+ set_button.grid(row=13, column=0, columnspan=2)
+
+ def create_db_edit_column(self):
+ self.db_frame = tk.Frame(self.root)
+ self.db_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ submenu_label = tk.Label(self.db_frame, text="Edit Database", font=("Arial", 12, "bold"))
+ submenu_label.grid(row=0, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.db_frame, orient='horizontal')
+ separator.grid(row=1, column=0, columnspan=2, sticky='ew')
+
+ # Position Database Selector
+ position_db_label = tk.Label(self.db_frame, text="Positions", font="Arial")
+ position_db_label.grid(row=2, column=0)
+ self.position_db_selector = ttk.Combobox(self.db_frame, values=[""], width=8)
+ self.position_db_selector.grid(row=2, column=1)
+ self.position_db_selector.bind('<>', self.position_db_selector_event)
+
+ # Position Delete Button
+ position_delete_button = tk.Button(self.db_frame, text="Delete Position", command=self.position_delete_event)
+ position_delete_button.grid(row=3, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.db_frame, orient='horizontal')
+ separator.grid(row=4, column=0, columnspan=2, sticky='ew')
+
+ # Grasp Database Selector
+ grasp_db_label = tk.Label(self.db_frame, text="Grasp", font="Arial")
+ grasp_db_label.grid(row=5, column=0)
+ self.grasp_db_selector = ttk.Combobox(self.db_frame, values=[""], width=8)
+ self.grasp_db_selector.grid(row=5, column=1)
+ self.grasp_db_selector.bind('<>', self.grasp_db_selector_event)
+
+ # Grasp Delete Button
+ grasp_delete_button = tk.Button(self.db_frame, text="Delete Grasp", command=self.grasp_delete_event)
+ grasp_delete_button.grid(row=6, column=0, columnspan=2)
+ #
+
+
+def main(args=None):
+ rclpy.init(args=args)
+ gui_node = GUINode()
+
+ thread_spin = threading.Thread(target=rclpy.spin, args=(gui_node,))
+ thread_spin.start()
+
+ root = tk.Tk()
+
+ # app = MyGUI(root)
+ app = MyGUI(root, gui_node)
+ while True:
+ app.update_from_database()
+ app.update_position_values()
+ root.update()
+
+ gui_node.destroy_node()
+ rclpy.shutdown()
+ app.conn.close()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/franka_logistics/position_run_gui.py b/franka_logistics/position_run_gui.py
new file mode 100644
index 0000000..094507f
--- /dev/null
+++ b/franka_logistics/position_run_gui.py
@@ -0,0 +1,439 @@
+import threading
+import tkinter as tk
+from tkinter import ttk
+
+import sqlite3
+
+import rclpy
+from rclpy.node import Node
+from rclpy.action import ActionClient
+
+from sensor_msgs.msg import JointState
+from franka_cps_msgs.action import GraspSequence
+
+
+class GUINode(Node):
+ def __init__(self):
+ super().__init__('position_run_gui_node')
+ self.execution_client = ActionClient(self, GraspSequence, 'franka_grasp/grasp_sequence')
+
+ self._send_goal_future = None
+ self._get_result_future = None
+
+ def send_grasp_sequence_request(self, idle, hower, grasp, drop):
+ goal_msg = GraspSequence.Goal()
+ #self.get_logger().info(f"New Grasp Sequence Request {idle, hower, grasp, drop}")
+ goal_msg.idle = idle
+ goal_msg.hower = hower
+ goal_msg.grasp = grasp
+ goal_msg.drop = drop
+
+ self.execution_client.wait_for_server()
+ self._send_goal_future = self.execution_client.send_goal_async(goal_msg, feedback_callback=self.feedback_callback)
+ self._send_goal_future.add_done_callback(self.goal_response_callback)
+
+ def feedback_callback(self, feedback_msg):
+ feedback = feedback_msg.feedback
+ self.get_logger().info(f'Feedback: {feedback.current_step}')
+
+ def goal_response_callback(self, future):
+ goal_handle = future.result()
+ if not goal_handle.accepted:
+ self.get_logger().info('Grasp Sequence Goal rejected!')
+ return
+
+ self.get_logger().info('Grasp Sequence Goal accepted!')
+ self._get_result_future = goal_handle.get_result_async()
+ self._get_result_future.add_done_callback(self.get_result_callback)
+
+ def get_result_callback(self, future):
+ result = future.result().result
+ success = result.success
+ message = result.error_code
+ self.get_logger().info(f'Grasp Sequence result - Success: {success}, ErrorCode: {message}')
+
+
+class MyGUI:
+ def __init__(self, root, node):
+ self.root = root
+ self.root.title("Franka Grasp Interface")
+
+ # ROS2 Node
+ self.node = node
+
+ # Connect to your SQLite database
+ self.conn = sqlite3.connect('/home/niko/sqlite_dbs/logistics_db')
+ self.cursor = self.conn.cursor()
+
+ # Track the current mode
+ self.current_mode = 'Run' # Starts in Learn mode
+
+ # Initialize frames for Learn and Run modes
+ self.perform_frame = ttk.Frame(root)
+
+ # Grasp Submenu attributes
+ self.grasp_name = ''
+ self.idle = ''
+ self.hower = ''
+ self.grasp = ''
+ self.drop = ''
+
+ # Database Submenu attributes
+ self.position_db = ''
+ self.grasp_db = ''
+
+ # Grasp Run Submenu attributes
+ self.grasp_run = ''
+ self.grasp_entries = {}
+ self.grasp_values = {
+ 'idle_position': 'idle',
+ 'hower_position': 'hower',
+ 'grasp_position': 'grasp',
+ 'drop_position': 'drop'
+ }
+
+ # GUI Subcomponents
+ self.grasp_name_entry = None
+ self.idle_selector = None
+ self.hower_selector = None
+ self.grasp_selector = None
+ self.drop_selector = None
+ self.width_grasp_entry = None
+ self.width_idle_entry = None
+
+ self.position_db_selector = None
+ self.grasp_db_selector = None
+
+ self.grasp_run_selector = None
+
+ # Frames
+ self.grasp_frame = None
+ self.db_frame = None
+ self.run_frame = None
+ self.separator_1 = None
+ self.separator_2 = None
+
+ # Initially set up Learn mode
+ self.setup_run_mode()
+
+ #
+ def setup_run_mode(self):
+ self.create_run_column()
+ self.separator_1 = tk.Canvas(self.root, width=1, bg="grey")
+ self.separator_1.pack(side=tk.LEFT, fill=tk.Y)
+ self.create_grasp_column()
+ self.separator_2 = tk.Canvas(self.root, width=1, bg="grey")
+ self.separator_2.pack(side=tk.LEFT, fill=tk.Y)
+ self.create_db_edit_column()
+
+ #
+
+ #
+ def get_position_names(self):
+ self.cursor.execute("SELECT position_name FROM position_table")
+ return [row[0] for row in self.cursor.fetchall()]
+
+ def get_grasp_names(self):
+ self.cursor.execute("SELECT sequence_name FROM position_sequence_table")
+ return [row[0] for row in self.cursor.fetchall()]
+
+ def get_position_by_name(self, position_name):
+ self.node.get_logger().info(f"Position:{position_name}")
+ self.cursor.execute(
+ "SELECT joint_1, joint_2, joint_3, joint_4, joint_5, joint_6, joint_7 FROM position_table WHERE position_name = ?",
+ (position_name,))
+ result = self.cursor.fetchone()
+ if result:
+ position = JointState()
+ self.node.get_logger().info(f"{result}")
+ position.position = result
+ return position
+ else:
+ self.node.get_logger().warn("Position not found")
+ return None
+
+ def get_grasp_sequence_by_name(self, sequence_name):
+ self.cursor.execute(
+ "SELECT position_idle, position_hower, position_grasp, position_drop"
+ " FROM position_sequence_table WHERE sequence_name = ?",
+ (sequence_name,))
+ result = self.cursor.fetchone()
+ if result:
+ self.grasp_values['idle_position'] = result[0]
+ self.grasp_values['hower_position'] = result[1]
+ self.grasp_values['grasp_position'] = result[2]
+ self.grasp_values['drop_position'] = result[3]
+ else:
+ print("Sequence not found")
+
+ def add_new_grasp(self, sequence_name, idle, hower, grasp, drop):
+ print(sequence_name, idle, hower, grasp, drop)
+ self.cursor.execute("INSERT INTO position_sequence_table"
+ """ (sequence_name, position_idle, position_hower, position_grasp, position_drop)
+ VALUES (?, ?, ?, ?, ?)""",
+ (sequence_name, idle, hower, grasp, drop))
+
+ self.conn.commit()
+
+ def delete_position_by_name(self, position_name):
+ self.cursor.execute("DELETE FROM position_table WHERE position_name = ?", (position_name,))
+
+ self.conn.commit()
+
+ def delete_grasp_by_name(self, sequence_name):
+ self.cursor.execute("DELETE FROM position_sequence_table WHERE sequence_name = ?", (sequence_name,))
+
+ self.conn.commit()
+
+ #
+
+ #
+ def update_grasp_values(self):
+ for key, value in self.grasp_values.items():
+ if key in self.grasp_entries:
+ self.grasp_entries[key].config(state='normal')
+ self.grasp_entries[key].delete(0, tk.END)
+ self.grasp_entries[key].insert(0, f"{value}")
+ self.grasp_entries[key].config(state='readonly')
+ def update_position_selectors(self, db_positions):
+ self.idle_selector['values'] = db_positions
+ self.hower_selector['values'] = db_positions
+ self.grasp_selector['values'] = db_positions
+ self.drop_selector['values'] = db_positions
+
+ def update_db_selectors(self, db_positions, db_grasp):
+ self.position_db_selector['values'] = db_positions
+ self.grasp_db_selector['values'] = db_grasp
+
+ def update_from_database(self):
+ db_positions = self.get_position_names()
+ db_grasps = self.get_grasp_names()
+ self.update_position_selectors(db_positions)
+ self.update_db_selectors(db_positions, db_grasps)
+ self.grasp_run_selector['values'] = db_grasps
+
+ #
+
+ #
+ # Position Events
+ def decimal_selector_event(self, event):
+ # Get the selected value from the decimal selector
+ self.decimal_select = int(self.decimal_selector.get())
+ print("Selected Decimal Value:", self.decimal_select)
+
+ # Grasp Events
+ def idle_selector_event(self, event):
+ self.idle = self.idle_selector.get()
+
+ def hower_selector_event(self, event):
+ self.hower = self.hower_selector.get()
+
+ def grasp_selector_event(self, event):
+ self.grasp = self.grasp_selector.get()
+
+ def drop_selector_event(self, event):
+ self.drop = self.drop_selector.get()
+
+ def grasp_set_event(self):
+ self.grasp_name = self.grasp_name_entry.get()
+ self.add_new_grasp(self.grasp_name,
+ self.idle,
+ self.hower,
+ self.grasp,
+ self.drop)
+ print("Grasp Button Clicked! Grasp Name:", self.grasp_name)
+
+ # Database Events
+ def position_db_selector_event(self, event):
+ self.position_db = self.position_db_selector.get()
+
+ def grasp_db_selector_event(self, event):
+ self.grasp_db = self.grasp_db_selector.get()
+
+ def position_delete_event(self):
+ self.delete_position_by_name(self.position_db)
+ print("Position: ", self.position_db, ' was deleted!')
+
+ def grasp_delete_event(self):
+ self.delete_grasp_by_name(self.grasp_db)
+ print("Grasp: ", self.grasp_db, ' was deleted!')
+
+ # Run Events
+ def grasp_run_selector_event(self, event):
+ self.grasp_run = self.grasp_run_selector.get()
+ self.get_grasp_sequence_by_name(self.grasp_run)
+
+ def grasp_perform_event(self):
+ idle = self.get_position_by_name(self.grasp_values['idle_position'] )
+ hower = self.get_position_by_name(self.grasp_values['hower_position'])
+ grasp = self.get_position_by_name(self.grasp_values['grasp_position'])
+ drop = self.get_position_by_name(self.grasp_values['drop_position'])
+
+ feedback = self.node.send_grasp_sequence_request(idle, hower, grasp, drop)
+
+ print("Grasp: ", self.grasp_run, ' is performed!')
+
+ #
+
+ #
+ def create_grasp_column(self):
+ self.grasp_frame = tk.Frame(self.root)
+ self.grasp_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ submenu_label = tk.Label(self.grasp_frame, text="New Grasp", font=("Arial", 12, "bold"))
+ submenu_label.grid(row=0, column=0, columnspan=2)
+
+ # Name Entry
+ tk.Label(self.grasp_frame, text="Grasp Name").grid(row=1, column=0)
+ self.grasp_name_entry = tk.Entry(self.grasp_frame)
+ self.grasp_name_entry.grid(row=1, column=1)
+
+ # Separator
+ separator = ttk.Separator(self.grasp_frame, orient='horizontal')
+ separator.grid(row=2, column=0, columnspan=2, sticky='ew')
+
+ # Title for Current Position
+ current_position_label = tk.Label(self.grasp_frame, text="Grasp Positions", font=("Arial", 10, "bold"))
+ current_position_label.grid(row=3, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.grasp_frame, orient='horizontal')
+ separator.grid(row=4, column=0, columnspan=2, sticky='ew')
+
+ # Idle Position Selector
+ idle_label = tk.Label(self.grasp_frame, text="Idle Position", font="Arial")
+ idle_label.grid(row=5, column=0)
+ self.idle_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.idle_selector.grid(row=5, column=1)
+ self.idle_selector.bind('<>', self.idle_selector_event)
+
+ # Hower Position Selector
+ hower_label = tk.Label(self.grasp_frame, text="Hower Position", font="Arial")
+ hower_label.grid(row=6, column=0)
+ self.hower_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.hower_selector.grid(row=6, column=1)
+ self.hower_selector.bind('<>', self.hower_selector_event)
+
+ # Grasp Position Selector
+ grasp_label = tk.Label(self.grasp_frame, text="Grasp Position", font="Arial")
+ grasp_label.grid(row=7, column=0)
+ self.grasp_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.grasp_selector.grid(row=7, column=1)
+ self.grasp_selector.bind('<>', self.grasp_selector_event)
+
+ # Drop Position Selector
+ drop_label = tk.Label(self.grasp_frame, text="Drop Position", font="Arial")
+ drop_label.grid(row=8, column=0)
+ self.drop_selector = ttk.Combobox(self.grasp_frame, values=[""], width=10)
+ self.drop_selector.grid(row=8, column=1)
+ self.drop_selector.bind('<>', self.drop_selector_event)
+
+ # Separator
+ separator = ttk.Separator(self.grasp_frame, orient='horizontal')
+ separator.grid(row=12, column=0, columnspan=2, sticky='ew')
+
+ # Set Button
+ set_button = tk.Button(self.grasp_frame, text="Set New Grasp", command=self.grasp_set_event)
+ set_button.grid(row=13, column=0, columnspan=2)
+
+ def create_db_edit_column(self):
+ self.db_frame = tk.Frame(self.root)
+ self.db_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ submenu_label = tk.Label(self.db_frame, text="Edit Database", font=("Arial", 12, "bold"))
+ submenu_label.grid(row=0, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.db_frame, orient='horizontal')
+ separator.grid(row=1, column=0, columnspan=2, sticky='ew')
+
+ # Position Database Selector
+ position_db_label = tk.Label(self.db_frame, text="Positions", font="Arial")
+ position_db_label.grid(row=2, column=0)
+ self.position_db_selector = ttk.Combobox(self.db_frame, values=[""], width=8)
+ self.position_db_selector.grid(row=2, column=1)
+ self.position_db_selector.bind('<>', self.position_db_selector_event)
+
+ # Position Delete Button
+ position_delete_button = tk.Button(self.db_frame, text="Delete Position", command=self.position_delete_event)
+ position_delete_button.grid(row=3, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.db_frame, orient='horizontal')
+ separator.grid(row=4, column=0, columnspan=2, sticky='ew')
+
+ # Grasp Database Selector
+ grasp_db_label = tk.Label(self.db_frame, text="Grasp", font="Arial")
+ grasp_db_label.grid(row=5, column=0)
+ self.grasp_db_selector = ttk.Combobox(self.db_frame, values=[""], width=8)
+ self.grasp_db_selector.grid(row=5, column=1)
+ self.grasp_db_selector.bind('<>', self.grasp_db_selector_event)
+
+ # Grasp Delete Button
+ grasp_delete_button = tk.Button(self.db_frame, text="Delete Grasp", command=self.grasp_delete_event)
+ grasp_delete_button.grid(row=6, column=0, columnspan=2)
+
+ def create_run_column(self):
+ self.run_frame = tk.Frame(self.root)
+ self.run_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+ submenu_label = tk.Label(self.run_frame, text="Run Grasp", font=("Arial", 12, "bold"))
+ submenu_label.grid(row=0, column=0, columnspan=2)
+
+ # Separator
+ separator = ttk.Separator(self.run_frame, orient='horizontal')
+ separator.grid(row=1, column=0, columnspan=2, sticky='ew')
+
+ # Position Database Selector
+ grasp_label = tk.Label(self.run_frame, text="Grasp", font="Arial")
+ grasp_label.grid(row=2, column=0)
+ self.grasp_run_selector = ttk.Combobox(self.run_frame, values=[""], width=8)
+ self.grasp_run_selector.grid(row=2, column=1)
+ self.grasp_run_selector.bind('<>', self.grasp_run_selector_event)
+
+ # Separator
+ separator = ttk.Separator(self.run_frame, orient='horizontal')
+ separator.grid(row=3, column=0, columnspan=2, sticky='ew')
+
+ grasp_labels = ['Idle Position', 'Hower Position', 'Grasp Position', 'Drop Position']
+
+ for i, label in enumerate(grasp_labels, start=4):
+ tk.Label(self.run_frame, text=label).grid(row=i, column=0)
+ entry = tk.Entry(self.run_frame, state='readonly', width=8)
+ entry.grid(row=i, column=1)
+ self.grasp_entries[label.replace(' ', '_').lower()] = entry
+
+ # Separator
+ separator = ttk.Separator(self.run_frame, orient='horizontal')
+ separator.grid(row=10, column=0, columnspan=2, sticky='ew')
+
+ # Grasp Perform Button
+ grasp_perform_button = tk.Button(self.run_frame, text="Perform Grasp", command=self.grasp_perform_event)
+ grasp_perform_button.grid(row=11, column=0, columnspan=2)
+
+ #
+
+
+def main(args=None):
+ rclpy.init(args=args)
+ gui_node = GUINode()
+
+ thread_spin = threading.Thread(target=rclpy.spin, args=(gui_node,))
+ thread_spin.start()
+
+ root = tk.Tk()
+
+ # app = MyGUI(root)
+ app = MyGUI(root, gui_node)
+ while True:
+ app.update_from_database()
+ app.update_grasp_values()
+ root.update()
+
+ gui_node.destroy_node()
+ rclpy.shutdown()
+ app.conn.close()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/launch/position_learn.launch.py b/launch/position_learn.launch.py
new file mode 100644
index 0000000..9039d8b
--- /dev/null
+++ b/launch/position_learn.launch.py
@@ -0,0 +1,146 @@
+import os
+
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import (DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription,
+ Shutdown)
+from launch.conditions import UnlessCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+import yaml
+
+
+def load_yaml(package_name, file_path):
+ package_path = get_package_share_directory(package_name)
+ absolute_file_path = os.path.join(package_path, file_path)
+
+ try:
+ with open(absolute_file_path, 'r') as file:
+ return yaml.safe_load(file)
+ except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
+ return None
+
+
+def generate_launch_description():
+ robot_ip_parameter_name = 'robot_ip'
+ use_fake_hardware_parameter_name = 'use_fake_hardware'
+ fake_sensor_commands_parameter_name = 'fake_sensor_commands'
+
+ robot_ip = LaunchConfiguration(robot_ip_parameter_name)
+ use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
+ fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
+
+ # planning_context
+ franka_xacro_file = os.path.join(get_package_share_directory('franka_description'), 'robots',
+ 'panda_arm.urdf.xacro')
+ robot_description_config = Command(
+ [FindExecutable(name='xacro'), ' ', franka_xacro_file, ' hand:=true',
+ ' robot_ip:=', robot_ip, ' use_fake_hardware:=', use_fake_hardware,
+ ' fake_sensor_commands:=', fake_sensor_commands])
+
+ robot_description = {'robot_description': robot_description_config}
+
+ # Publish TF
+ robot_state_publisher = Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ name='robot_state_publisher',
+ output='both',
+ parameters=[robot_description],
+ )
+
+ ros2_controllers_path = os.path.join(
+ get_package_share_directory('franka_moveit_config'),
+ 'config',
+ 'panda_ros_controllers.yaml',
+ )
+ ros2_control_node = Node(
+ package='controller_manager',
+ executable='ros2_control_node',
+ parameters=[robot_description, ros2_controllers_path],
+ remappings=[('joint_states', 'franka/joint_states')],
+ output={
+ 'stdout': 'screen',
+ 'stderr': 'screen',
+ },
+ on_exit=Shutdown(),
+ )
+
+ load_controllers = []
+ for controller in ['joint_state_broadcaster']:
+ load_controllers += [
+ ExecuteProcess(
+ cmd=['ros2 run controller_manager spawner {}'.format(controller)],
+ shell=True,
+ output='screen',
+ )
+ ]
+
+ joint_state_publisher = Node(
+ package='joint_state_publisher',
+ executable='joint_state_publisher',
+ name='joint_state_publisher',
+ parameters=[
+ {'source_list': ['franka/joint_states', 'panda_gripper/joint_states'], 'rate': 30}],
+ )
+
+ franka_robot_state_broadcaster = Node(
+ package='controller_manager',
+ executable='spawner',
+ arguments=['franka_robot_state_broadcaster'],
+ output='screen',
+ condition=UnlessCondition(use_fake_hardware),
+ )
+
+ franka_grasp_demo = Node(
+ package='franka_logistics',
+ executable='franka_grasp_demo',
+ name='franka_grasp_demo',
+ output='screen',
+ )
+
+ franka_gui = Node(
+ package='franka_logistics',
+ executable='franka_position_learn_gui',
+ name='franka_position_learn_gui',
+ output='screen',
+ )
+
+ robot_arg = DeclareLaunchArgument(
+ robot_ip_parameter_name,
+ default_value='192.168.1.211',
+ description='Hostname or IP address of the robot.')
+
+ use_fake_hardware_arg = DeclareLaunchArgument(
+ use_fake_hardware_parameter_name,
+ default_value='false',
+ description='Use fake hardware')
+ fake_sensor_commands_arg = DeclareLaunchArgument(
+ fake_sensor_commands_parameter_name,
+ default_value='false',
+ description="Fake sensor commands. Only valid when '{}' is true".format(
+ use_fake_hardware_parameter_name))
+
+ gripper_launch_file = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([PathJoinSubstitution(
+ [FindPackageShare('franka_gripper'), 'launch', 'gripper.launch.py'])]),
+ launch_arguments={'robot_ip': robot_ip,
+ use_fake_hardware_parameter_name: use_fake_hardware}.items(),
+ )
+
+ return LaunchDescription(
+ [
+ robot_arg,
+ use_fake_hardware_arg,
+ fake_sensor_commands_arg,
+ robot_state_publisher,
+ joint_state_publisher,
+ franka_gui,
+ franka_grasp_demo,
+ ros2_control_node,
+ franka_robot_state_broadcaster,
+ gripper_launch_file
+ ] + load_controllers
+ )
\ No newline at end of file
diff --git a/launch/position_run.launch.py b/launch/position_run.launch.py
new file mode 100644
index 0000000..a7b563b
--- /dev/null
+++ b/launch/position_run.launch.py
@@ -0,0 +1,146 @@
+import os
+
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import (DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription,
+ Shutdown)
+from launch.conditions import UnlessCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+import yaml
+
+
+def load_yaml(package_name, file_path):
+ package_path = get_package_share_directory(package_name)
+ absolute_file_path = os.path.join(package_path, file_path)
+
+ try:
+ with open(absolute_file_path, 'r') as file:
+ return yaml.safe_load(file)
+ except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
+ return None
+
+
+def generate_launch_description():
+ robot_ip_parameter_name = 'robot_ip'
+ use_fake_hardware_parameter_name = 'use_fake_hardware'
+ fake_sensor_commands_parameter_name = 'fake_sensor_commands'
+
+ robot_ip = LaunchConfiguration(robot_ip_parameter_name)
+ use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
+ fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
+
+ # planning_context
+ franka_xacro_file = os.path.join(get_package_share_directory('franka_description'), 'robots',
+ 'panda_arm.urdf.xacro')
+ robot_description_config = Command(
+ [FindExecutable(name='xacro'), ' ', franka_xacro_file, ' hand:=true',
+ ' robot_ip:=', robot_ip, ' use_fake_hardware:=', use_fake_hardware,
+ ' fake_sensor_commands:=', fake_sensor_commands])
+
+ robot_description = {'robot_description': robot_description_config}
+
+ # Publish TF
+ robot_state_publisher = Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ name='robot_state_publisher',
+ output='both',
+ parameters=[robot_description],
+ )
+
+ ros2_controllers_path = os.path.join(
+ get_package_share_directory('franka_moveit_config'),
+ 'config',
+ 'panda_ros_controllers.yaml',
+ )
+ ros2_control_node = Node(
+ package='controller_manager',
+ executable='ros2_control_node',
+ parameters=[robot_description, ros2_controllers_path],
+ remappings=[('joint_states', 'franka/joint_states')],
+ output={
+ 'stdout': 'screen',
+ 'stderr': 'screen',
+ },
+ on_exit=Shutdown(),
+ )
+
+ load_controllers = []
+ for controller in ['panda_arm_controller', 'joint_state_broadcaster']:
+ load_controllers += [
+ ExecuteProcess(
+ cmd=['ros2 run controller_manager spawner {}'.format(controller)],
+ shell=True,
+ output='screen',
+ )
+ ]
+
+ joint_state_publisher = Node(
+ package='joint_state_publisher',
+ executable='joint_state_publisher',
+ name='joint_state_publisher',
+ parameters=[
+ {'source_list': ['franka/joint_states', 'panda_gripper/joint_states'], 'rate': 30}],
+ )
+
+ franka_robot_state_broadcaster = Node(
+ package='controller_manager',
+ executable='spawner',
+ arguments=['franka_robot_state_broadcaster'],
+ output='screen',
+ condition=UnlessCondition(use_fake_hardware),
+ )
+
+ franka_grasp_demo = Node(
+ package='franka_logistics',
+ executable='franka_grasp_demo',
+ name='franka_grasp_demo',
+ output='screen',
+ )
+
+ franka_gui = Node(
+ package='franka_logistics',
+ executable='franka_position_run_gui',
+ name='franka_position_run_gui',
+ output='screen',
+ )
+
+ robot_arg = DeclareLaunchArgument(
+ robot_ip_parameter_name,
+ default_value='192.168.1.211',
+ description='Hostname or IP address of the robot.')
+
+ use_fake_hardware_arg = DeclareLaunchArgument(
+ use_fake_hardware_parameter_name,
+ default_value='false',
+ description='Use fake hardware')
+ fake_sensor_commands_arg = DeclareLaunchArgument(
+ fake_sensor_commands_parameter_name,
+ default_value='false',
+ description="Fake sensor commands. Only valid when '{}' is true".format(
+ use_fake_hardware_parameter_name))
+
+ gripper_launch_file = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([PathJoinSubstitution(
+ [FindPackageShare('franka_gripper'), 'launch', 'gripper.launch.py'])]),
+ launch_arguments={'robot_ip': robot_ip,
+ use_fake_hardware_parameter_name: use_fake_hardware}.items(),
+ )
+
+ return LaunchDescription(
+ [
+ robot_arg,
+ use_fake_hardware_arg,
+ fake_sensor_commands_arg,
+ robot_state_publisher,
+ joint_state_publisher,
+ franka_gui,
+ franka_grasp_demo,
+ ros2_control_node,
+ franka_robot_state_broadcaster,
+ gripper_launch_file
+ ] + load_controllers
+ )
\ No newline at end of file
diff --git a/package.xml b/package.xml
index 5288a37..d106380 100644
--- a/package.xml
+++ b/package.xml
@@ -11,7 +11,10 @@
geometry_msgs
moveit_msgs
std_msgs
+ sensor_msgs
franka_cps_msgs
+ control_msgs
+ trajectory_msgs
ament_copyright
ament_flake8
diff --git a/setup.py b/setup.py
index 7f2742d..4cc5140 100644
--- a/setup.py
+++ b/setup.py
@@ -13,6 +13,7 @@ setup(
['resource/' + package_name]),
(os.path.join('share', package_name), glob('launch/*.launch.py')),
('share/' + package_name, ['package.xml']),
+ (os.path.join('share', package_name), glob('launch/*.launch.py')),
],
install_requires=['setuptools'],
zip_safe=True,
@@ -27,7 +28,10 @@ setup(
'pose_learn = franka_logistics.pose_learn_node:main',
'franka_grasp_gui = franka_logistics.GUI:main',
'franka_pose_learn_gui = franka_logistics.Pose_Learn_GUI:main',
- 'franka_run_gui = franka_logistics.Run_GUI:main'
+ 'franka_run_gui = franka_logistics.Run_GUI:main',
+ 'franka_position_learn_gui = franka_logistics.position_learn_gui:main',
+ 'franka_position_run_gui = franka_logistics.position_run_gui:main',
+ 'franka_grasp_demo = franka_logistics.grasp_demo_node:main'
],
},
)