From ec147c9df609ac8079337eca0fe2a922b91716e9 Mon Sep 17 00:00:00 2001 From: m-guberina <gubi.guberina@gmail.com> Date: Tue, 11 Mar 2025 12:07:13 +0100 Subject: [PATCH] can navigate mobile yumi --- examples/navigation/mobile_base_navigation.py | 8 +- examples/ros/dummy_cmds_pub.py | 19 +++- examples/ros/navigation_example.py | 99 +++++++++++++++---- .../smc/robots/implementations/mobile_yumi.py | 26 +++-- 4 files changed, 118 insertions(+), 34 deletions(-) diff --git a/examples/navigation/mobile_base_navigation.py b/examples/navigation/mobile_base_navigation.py index 446cd99..05897d4 100644 --- a/examples/navigation/mobile_base_navigation.py +++ b/examples/navigation/mobile_base_navigation.py @@ -162,7 +162,13 @@ if __name__ == "__main__": path_planner = partial(fixedPathPlanner, [0], path2D) # CrocoBasePathFollowingMPC(args, robot, x0, path_planner) - cartesianPathFollowingWithPlanner(args, robot, path_planner, 0.0) + loop = cartesianPathFollowingWithPlanner(args, robot, path_planner, 0.0, run=False) + breakFlag = False + import time + + while not breakFlag: + loop.run_one_iter(loop.current_iteration) + time.sleep(robot._dt) # MPCWithCLIKFallback(args, robot, path_planner) if args.real: diff --git a/examples/ros/dummy_cmds_pub.py b/examples/ros/dummy_cmds_pub.py index 8b68dbc..2a42f46 100755 --- a/examples/ros/dummy_cmds_pub.py +++ b/examples/ros/dummy_cmds_pub.py @@ -65,6 +65,12 @@ def get_args(): default=False, help="publish or not", ) + parser.add_argument( + "--sim", + type=float, + help="if in sim you need to set sim time, otherwise not", + default=3.0, + ) # parser.add_argument('--ros-args', action='extend', nargs="+") # parser.add_argument('-r', action='extend', nargs="+") parser.add_argument("--ros-args", action="append", nargs="*") @@ -146,7 +152,6 @@ class DummyNode(Node): JointState, f"{self._ns}/platform/joints_cmd", 1 ) self.robot.set_publisher_joints_cmd(self._cmd_pub) - # self._cmd_pub = self.create_publisher(JointState, f"{self._ns}joints_cmd", 1) self.sub_base_odom = self.create_subscription( Odometry, f"{self._ns}/platform/odometry", self.callback_base_odom, 1 ) @@ -199,11 +204,15 @@ class DummyNode(Node): ] ) th = 2 * np.arctan2(sinth2, costh2) + if np.linalg.norm(self.init_odom) < 1e-4: + self.init_odom[0] = msg.pose.pose.position.x + self.init_odom[1] = msg.pose.pose.position.y + self.init_odom[2] = th if self.args.unreal: - self.robot._q[0] = msg.pose.pose.position.x - self.robot._q[1] = msg.pose.pose.position.y - self.robot._q[2] = np.cos(th) - self.robot._q[3] = np.sin(th) + self.robot._q[0] = msg.pose.pose.position.x - self.init_odom[0] + self.robot._q[1] = msg.pose.pose.position.y - self.init_odom[1] + self.robot._q[2] = np.cos(th - self.init_odom[2]) + self.robot._q[3] = np.sin(th - self.init_odom[2]) # self.get_logger().info("ODOM CALLBACK" + str(msg)) def callback_arms_state(self, msg: JointState): diff --git a/examples/ros/navigation_example.py b/examples/ros/navigation_example.py index f20b10a..f1c225a 100644 --- a/examples/ros/navigation_example.py +++ b/examples/ros/navigation_example.py @@ -21,6 +21,7 @@ from sensor_msgs.msg import JointState from nav_msgs.msg import Odometry from abb_python_utilities.names import get_rosified_name from rclpy.callback_groups import ReentrantCallbackGroup +from rclpy.time import Time import numpy as np import argparse @@ -44,6 +45,10 @@ from smc.control.optimal_control.croco_path_following.mpc.base_reference_mpc imp from smc.util.draw_path import drawPath from smc.control.optimal_control.util import get_OCP_args from smc.robots.utils import getRobotFromArgs +from smc.control.cartesian_space.cartesian_space_trajectory_following import ( + cartesianPathFollowingWithPlanner, +) + import pinocchio as pin import matplotlib from functools import partial @@ -114,6 +119,12 @@ def get_args(): help="height of the map in meters (y-axis) - only used for drawing of the path", default=3.0, ) + parser.add_argument( + "--sim", + action=argparse.BooleanOptionalAction, + help="if in sim you need to set sim time, otherwise not", + default=True, + ) # parser.add_argument('--ros-args', action='extend', nargs="+") # parser.add_argument('-r', action='extend', nargs="+") parser.add_argument("--ros-args", action="append", nargs="*") @@ -137,6 +148,15 @@ class DummyNode(Node): self, args, robot: RealMobileYumiRobotManager, loop_manager: ControlLoopManager ): super().__init__("dummy_cmds_pub_node") + if args.sim: + self.set_parameters( + [ + rclpy.parameter.Parameter( + "use_sim_time", rclpy.Parameter.Type.BOOL, True + ) + ] + ) + self.wait_for_sim_time() self.robot = robot self.loop_manager = loop_manager self.args = args @@ -191,8 +211,7 @@ class DummyNode(Node): JointState, f"{self._ns}/platform/joints_cmd", 1 ) self.get_logger().info(f"{self._ns}/platform/joints_cmd") - self.robot.set_publisher_joints_cmd(self._cmd_pub) - self._cmd_pub = self.create_publisher(JointState, f"{self._ns}joints_cmd", 1) + # self.robot.set_publisher_joints_cmd(self._cmd_pub) self.sub_base_odom = self.create_subscription( Odometry, f"{self._ns}/platform/odometry", self.callback_base_odom, 1 ) @@ -214,14 +233,25 @@ class DummyNode(Node): breakFlag = self.loop_manager.run_one_iter( self.loop_manager.current_iteration ) + # self.get_logger().info("TIMER_CMD") - # msg = self.empty_msg - # for i in range(3): - # msg.velocity[i] = self.robot._v[i] - # for i in range(15, 29): - # msg.velocity[i] = self.robot._v[i - 12] - # self.get_logger().info(str(self.robot._v)) - # self._cmd_pub.publish(msg) + if self.args.unreal: + msg = self.empty_msg + msg.header.stamp = Time().to_msg() + + # TEST + # msg.velocity[2] = ( + # np.sin(self.loop_manager.current_iteration / (self.args.ctrl_freq * 3)) + # / 10 + # ) + # REAL + for i in range(3): + msg.velocity[i] = self.robot._v_cmd[i] + for i in range(15, 29): + msg.velocity[i] = self.robot._v_cmd[i - 12] + self.get_logger().info(str(msg)) + self.get_logger().info(str(self.robot._v_cmd)) + self._cmd_pub.publish(msg) def callback_base_odom(self, msg: Odometry): # self.robot._v[0] = msg.twist.twist.linear.x @@ -239,22 +269,47 @@ class DummyNode(Node): ] ) th = 2 * np.arctan2(sinth2, costh2) - if np.linalg.norm(self.init_odom) < 1e-4: + if ( + np.linalg.norm(self.init_odom) < 1e-4 + ) and self.loop_manager.current_iteration < 10: self.init_odom[0] = msg.pose.pose.position.x self.init_odom[1] = msg.pose.pose.position.y self.init_odom[2] = th if self.args.unreal: - self.robot._q[0] = msg.pose.pose.position.x - self.init_odom[0] - self.robot._q[1] = msg.pose.pose.position.y - self.init_odom[1] + T_odom = np.zeros((3, 3)) + T_odom[0, 0] = np.cos(self.init_odom[2]) + T_odom[0, 1] = -1 * np.sin(self.init_odom[2]) + T_odom[0, 2] = self.init_odom[0] + T_odom[1, 0] = np.sin(self.init_odom[2]) + T_odom[1, 1] = np.cos(self.init_odom[2]) + T_odom[1, 2] = self.init_odom[1] + T_odom[2, 2] = 1.0 + p_odom = np.array([msg.pose.pose.position.x, msg.pose.pose.position.y, 1]) + T_inv_odom = np.zeros((3, 3)) + T_inv_odom[:2, :2] = T_odom[:2, :2].T + T_inv_odom[:2, 2] = (-1 * T_odom[:2, :2].T) @ T_odom[:2, 2] + T_inv_odom[2, 2] = 0.0 + p_ctrl = T_inv_odom @ p_odom + self.robot._q[0] = p_ctrl[0] + self.robot._q[1] = p_ctrl[1] self.robot._q[2] = np.cos(th - self.init_odom[2]) self.robot._q[3] = np.sin(th - self.init_odom[2]) + # self.get_logger().info("CALLBACK_ODOM") + self.get_logger().info(str(self.robot._q[:4])) + # self.get_logger().info(str(self.init_odom)) def callback_arms_state(self, msg: JointState): - if self.args.unreal: - if self.loop_manager.current_iteration < 200 or self.args.unreal: - self.robot._q[-14:-7] = msg.position[-14:-7] - self.robot._q[-7:] = msg.position[-7:] - # self.get_logger().info(str(self.loop_manager.current_iteration)) + if self.loop_manager.current_iteration < 200 or self.args.unreal: + self.robot._q[-14:-7] = msg.position[-14:-7] + self.robot._q[-7:] = msg.position[-7:] + # self.get_logger().info("CALLBACK_JOINT_STATE") + + def wait_for_sim_time(self): + """Wait for the /clock topic to start publishing.""" + self.get_logger().info("Waiting for simulated time to be active...") + while not self.get_clock().now().nanoseconds > 0: + rclpy.spin_once(self, timeout_sec=0.1) + self.get_logger().info("Simulated time is now active!") # def main(args=None): @@ -294,9 +349,13 @@ def main(args=None): robot.updateViz({"fixed_path": path2D}) path_planner = partial(fixedPathPlanner, [0], path2D) - x0 = np.concatenate([robot.q, robot.v]) - loop_manager = CrocoBasePathFollowingMPC( - args_smc, robot, x0, path_planner, run=False + # x0 = np.concatenate([robot.q, robot.v]) + # loop_manager = CrocoBasePathFollowingMPC( + # args_smc, robot, x0, path_planner, run=False + # ) + robot._mode = robot.control_mode.base_only + loop_manager = cartesianPathFollowingWithPlanner( + args_smc, robot, path_planner, 0.0, run=False ) rclpy.init(args=args) diff --git a/python/smc/robots/implementations/mobile_yumi.py b/python/smc/robots/implementations/mobile_yumi.py index 35671a7..602fdee 100644 --- a/python/smc/robots/implementations/mobile_yumi.py +++ b/python/smc/robots/implementations/mobile_yumi.py @@ -103,9 +103,15 @@ class SimulatedMobileYuMiRobotManager( ) # pin.RandomConfiguration does not work well for planar joint, # or at least i remember something along those lines being the case - self._q[0] = np.random.random() - self._q[1] = np.random.random() - theta = np.random.random() * 2 * np.pi - np.pi + # self._q[0] = np.random.random() + # self._q[1] = np.random.random() + # theta = np.random.random() * 2 * np.pi - np.pi + # self._q[2] = np.cos(theta) + # self._q[3] = np.sin(theta) + + self._q[0] = np.random.random() * 0.0 + self._q[1] = np.random.random() * 0.0 + theta = np.random.random() * 0.0 self._q[2] = np.cos(theta) self._q[3] = np.sin(theta) @@ -113,6 +119,7 @@ class SimulatedMobileYuMiRobotManager( class RealMobileYumiRobotManager(AbstractMobileYuMiRobotManager): def __init__(self, args): super().__init__(args) + self._v_cmd = np.zeros(self.model.nv) # TODO: here assert you need to have ros2 installed to run on real heron # and then set all this bullshit here instead of elsewhere @@ -125,7 +132,7 @@ class RealMobileYumiRobotManager(AbstractMobileYuMiRobotManager): if self.args.unreal: if any(np.isnan(v)): v = np.zeros(self.model.nv) - self._v = v + self._v_cmd = v # empty_msg = JointState() # for i in range(29): # empty_msg.velocity.append(0.0) @@ -152,7 +159,7 @@ class RealMobileYumiRobotManager(AbstractMobileYuMiRobotManager): pass def stopRobot(self): - self.sendVelocityCommand(np.zeros(self.model.nv)) + self.sendVelocityCommand(np.zeros(self.nv)) def setFreedrive(self): pass @@ -169,9 +176,12 @@ class RealMobileYumiRobotManager(AbstractMobileYuMiRobotManager): ) # pin.RandomConfiguration does not work well for planar joint, # or at least i remember something along those lines being the case - self._q[0] = np.random.random() * 0.1 - self._q[1] = np.random.random() * 0.1 - theta = np.random.random() * 2 * np.pi - np.pi + # self._q[0] = np.random.random() * 0.1 + # self._q[1] = np.random.random() * 0.1 + # theta = np.random.random() * 2 * np.pi - np.pi + self._q[0] = np.random.random() * 0.0 + self._q[1] = np.random.random() * 0.0 + theta = np.random.random() * 0.0 self._q[2] = np.cos(theta) self._q[3] = np.sin(theta) -- GitLab