INIT: working robo-arm
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
build/
|
||||
install/
|
||||
log/
|
||||
@@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(arm_simulation)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
# find dependencies
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(rclpy REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
find_package(trajectory_msgs REQUIRED)
|
||||
|
||||
install(
|
||||
DIRECTORY launch urdf config scripts
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
# Install Python scripts directly to the lib folder so 'ros2 run' can find them
|
||||
install(PROGRAMS
|
||||
scripts/move_arm.py
|
||||
DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
# the following line skips the linter which checks for copyrights
|
||||
# comment the line when a copyright and license is added to all source files
|
||||
set(ament_cmake_copyright_FOUND TRUE)
|
||||
# the following line skips cpplint (only works in a git repo)
|
||||
# comment the line when this package is in a git repo and when
|
||||
# a copyright and license is added to all source files
|
||||
set(ament_cmake_cpplint_FOUND TRUE)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
@@ -0,0 +1,24 @@
|
||||
controller_manager:
|
||||
ros__parameters:
|
||||
update_rate: 100 # Hz
|
||||
use_sim_time: true
|
||||
|
||||
joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
|
||||
arm_controller:
|
||||
type: joint_trajectory_controller/JointTrajectoryController
|
||||
|
||||
arm_controller:
|
||||
ros__parameters:
|
||||
joints:
|
||||
- joint_1
|
||||
- joint_2
|
||||
- joint_3
|
||||
- joint_4
|
||||
command_interfaces:
|
||||
- position
|
||||
state_interfaces:
|
||||
- position
|
||||
- velocity
|
||||
use_sim_time: true
|
||||
@@ -0,0 +1,107 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import IncludeLaunchDescription, RegisterEventHandler
|
||||
from launch.event_handlers import OnProcessExit
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch_ros.actions import Node
|
||||
import xacro
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# 1. Define package name and paths
|
||||
package_name = "arm_simulation"
|
||||
pkg_share = get_package_share_directory(package_name)
|
||||
|
||||
# Path to your XACRO file
|
||||
xacro_file = os.path.join(pkg_share, "urdf", "arm.urdf.xacro")
|
||||
|
||||
# Process XACRO to raw URDF string
|
||||
robot_description_config = xacro.process_file(xacro_file)
|
||||
robot_description = {"robot_description": robot_description_config.toxml()}
|
||||
|
||||
# 2. Robot State Publisher Node
|
||||
# This broadcasts the robot's coordinate frames and transforms (TF)
|
||||
node_robot_state_publisher = Node(
|
||||
package="robot_state_publisher",
|
||||
executable="robot_state_publisher",
|
||||
output="screen",
|
||||
parameters=[robot_description],
|
||||
)
|
||||
|
||||
# 3. Gazebo Harmonic Launch
|
||||
# Includes the official modern Gazebo launch script.
|
||||
# '-r' tells Gazebo to unpause the physics engine automatically on startup.
|
||||
gazebo = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(
|
||||
get_package_share_directory("ros_gz_sim"),
|
||||
"launch",
|
||||
"gz_sim.launch.py",
|
||||
)
|
||||
),
|
||||
launch_arguments={"gz_args": "-r empty.sdf"}.items(),
|
||||
)
|
||||
|
||||
# 4. Spawn Robot Node (Modern Gazebo Creator)
|
||||
# Replaces the old 'spawn_entity.py'. It grabs the URDF via the /robot_description topic.
|
||||
spawn_entity = Node(
|
||||
package="ros_gz_sim",
|
||||
executable="create",
|
||||
arguments=["-topic", "robot_description", "-name", "simple_arm"],
|
||||
output="screen",
|
||||
)
|
||||
|
||||
# 5. Joint State Broadcaster Spawner
|
||||
joint_state_broadcaster_spawner = Node(
|
||||
package="controller_manager",
|
||||
executable="spawner",
|
||||
arguments=[
|
||||
"joint_state_broadcaster",
|
||||
"--controller-manager",
|
||||
"/controller_manager",
|
||||
],
|
||||
)
|
||||
|
||||
# 6. Arm Controller Spawner
|
||||
# Loads the trajectory controller managing your arm's joint configuration.
|
||||
arm_controller_spawner = Node(
|
||||
package="controller_manager",
|
||||
executable="spawner",
|
||||
arguments=[
|
||||
"arm_controller",
|
||||
"--controller-manager",
|
||||
"/controller_manager",
|
||||
],
|
||||
)
|
||||
|
||||
# --- Event Handlers (Timing Control) ---
|
||||
# We must delay activating controllers until the Gazebo simulator is fully running
|
||||
# and has registered the 'ros2_control' plugin manager inside the robot entity.
|
||||
|
||||
# Delay loading joint_state_broadcaster until AFTER the robot is physically spawned
|
||||
delay_joint_state_broadcaster = RegisterEventHandler(
|
||||
event_handler=OnProcessExit(
|
||||
target_action=spawn_entity,
|
||||
on_exit=[joint_state_broadcaster_spawner],
|
||||
)
|
||||
)
|
||||
|
||||
# Delay loading arm_controller until AFTER the joint_state_broadcaster is fully active
|
||||
delay_arm_controller = RegisterEventHandler(
|
||||
event_handler=OnProcessExit(
|
||||
target_action=joint_state_broadcaster_spawner,
|
||||
on_exit=[arm_controller_spawner],
|
||||
)
|
||||
)
|
||||
|
||||
# Return the launch description with all processes grouped
|
||||
return LaunchDescription(
|
||||
[
|
||||
node_robot_state_publisher,
|
||||
gazebo,
|
||||
spawn_entity,
|
||||
delay_joint_state_broadcaster,
|
||||
delay_arm_controller,
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>arm_simulation</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="marcin.matczak@student.put.poznan.pl">marcin</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclpy</depend>
|
||||
<depend>std_msgs</depend>
|
||||
<depend>trajectory_msgs</depend>
|
||||
<depend>ros_gz_sim</depend>
|
||||
<depend>gz_ros2_control</depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
|
||||
from builtin_interfaces.msg import Duration
|
||||
|
||||
class ArmTrajectoryPublisher(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('arm_trajectory_publisher')
|
||||
|
||||
# 1. Create a publisher targeting the trajectory controller topic
|
||||
self.publisher_ = self.create_publisher(
|
||||
JointTrajectory,
|
||||
'/arm_controller/joint_trajectory',
|
||||
10
|
||||
)
|
||||
|
||||
# 2. Wait 2 seconds to make sure connections are secure, then execute
|
||||
self.timer = self.create_timer(2.0, self.move_robot_arm)
|
||||
self.get_logger().info('Arm controller node initialized. Waiting to send command...')
|
||||
|
||||
def move_robot_arm(self):
|
||||
# Cancel the timer so this command only sends once
|
||||
self.timer.cancel()
|
||||
|
||||
# 3. Initialize the trajectory message structure
|
||||
msg = JointTrajectory()
|
||||
|
||||
# These names MUST match the joint names defined in your URDF and YAML files exactly
|
||||
msg.joint_names = ['joint_1', 'joint_2', 'joint_3', 'joint_4']
|
||||
|
||||
# 4. Create a waypoint goal
|
||||
point = JointTrajectoryPoint()
|
||||
|
||||
# Target positions in Radians (joint_1 = 90 degrees, joint_2 = 45 degrees)
|
||||
# Example position: joint_1=0.0, joint_2=0.5, joint_3=0.5, joint_4=-0.5
|
||||
point.positions = [0.0, 0.5, 0.5, -0.5]
|
||||
|
||||
# Tell the physics engine to take exactly 4.0 seconds to execute this transition smoothly
|
||||
point.time_from_start = Duration(sec=4, nanosec=0)
|
||||
|
||||
# Append the waypoint to the trajectory list
|
||||
msg.points.append(point)
|
||||
|
||||
# 5. Publish the message to Gazebo
|
||||
self.get_logger().info('Sending smooth trajectory command to joint_1 and joint_2!')
|
||||
self.publisher_.publish(msg)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
node = ArmTrajectoryPublisher()
|
||||
rclpy.spin(node)
|
||||
node.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot name="four_axis_arm" xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<material name="blue"><color rgba="0.0 0.0 0.8 1.0"/></material>
|
||||
<material name="grey"><color rgba="0.5 0.5 0.5 1.0"/></material>
|
||||
<material name="black"><color rgba="0.1 0.1 0.1 1.0"/></material>
|
||||
<material name="red"><color rgba="0.8 0.0 0.0 1.0"/></material>
|
||||
|
||||
<link name="world"/>
|
||||
|
||||
<joint name="virtual_joint" type="fixed">
|
||||
<parent link="world"/>
|
||||
<child link="base_link"/>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
</joint>
|
||||
|
||||
<link name="base_link">
|
||||
<visual>
|
||||
<geometry><cylinder radius="0.2" length="0.1"/></geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry><cylinder radius="0.2" length="0.1"/></geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="5.0"/>
|
||||
<inertia ixx="0.05" ixy="0.0" ixz="0.0" iyy="0.05" iyz="0.0" izz="0.1"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="joint_1" type="revolute">
|
||||
<parent link="base_link"/>
|
||||
<child link="link_1"/>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit lower="-3.14" upper="3.14" effort="100.0" velocity="2.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="link_1">
|
||||
<visual>
|
||||
<origin xyz="0 0 0.2" rpy="0 0 0"/>
|
||||
<geometry><cylinder radius="0.08" length="0.4"/></geometry>
|
||||
<material name="blue"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.2" rpy="0 0 0"/>
|
||||
<geometry><cylinder radius="0.08" length="0.4"/></geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.2"/>
|
||||
<mass value="2.0"/>
|
||||
<inertia ixx="0.03" ixy="0.0" ixz="0.0" iyy="0.03" iyz="0.0" izz="0.006"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="joint_2" type="revolute">
|
||||
<parent link="link_1"/>
|
||||
<child link="link_2"/>
|
||||
<origin xyz="0 0 0.4" rpy="0 0 0"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit lower="-1.57" upper="1.57" effort="100.0" velocity="2.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="link_2">
|
||||
<visual>
|
||||
<origin xyz="0 0 0.2" rpy="0 0 0"/>
|
||||
<geometry><box size="0.06 0.06 0.4"/></geometry>
|
||||
<material name="grey"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.2" rpy="0 0 0"/>
|
||||
<geometry><box size="0.06 0.06 0.4"/></geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.2"/>
|
||||
<mass value="1.5"/>
|
||||
<inertia ixx="0.02" ixy="0.0" ixz="0.0" iyy="0.02" iyz="0.0" izz="0.001"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="joint_3" type="revolute">
|
||||
<parent link="link_2"/>
|
||||
<child link="link_3"/>
|
||||
<origin xyz="0 0 0.4" rpy="0 0 0"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit lower="-2.0" upper="2.0" effort="100.0" velocity="2.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="link_3">
|
||||
<visual>
|
||||
<origin xyz="0 0 0.15" rpy="0 0 0"/>
|
||||
<geometry><box size="0.05 0.05 0.3"/></geometry>
|
||||
<material name="blue"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.15" rpy="0 0 0"/>
|
||||
<geometry><box size="0.05 0.05 0.3"/></geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.15"/>
|
||||
<mass value="1.0"/>
|
||||
<inertia ixx="0.008" ixy="0.0" ixz="0.0" iyy="0.008" iyz="0.0" izz="0.0004"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="joint_4" type="revolute">
|
||||
<parent link="link_3"/>
|
||||
<child link="link_4"/>
|
||||
<origin xyz="0 0 0.3" rpy="0 0 0"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit lower="-1.57" upper="1.57" effort="50.0" velocity="3.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="link_4">
|
||||
<visual>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<geometry><cylinder radius="0.04" length="0.1"/></geometry>
|
||||
<material name="red"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<geometry><cylinder radius="0.04" length="0.1"/></geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.05"/>
|
||||
<mass value="0.5"/>
|
||||
<inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.0004"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
|
||||
<ros2_control name="GazeboSimSystem" type="system">
|
||||
<hardware>
|
||||
<plugin>gz_ros2_control/GazeboSimSystem</plugin>
|
||||
</hardware>
|
||||
|
||||
<joint name="joint_1">
|
||||
<command_interface name="position"><param name="min">-3.14</param><param name="max">3.14</param></command_interface>
|
||||
<state_interface name="position"><param name="initial_value">0.0</param></state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="joint_2">
|
||||
<command_interface name="position"><param name="min">-1.57</param><param name="max">1.57</param></command_interface>
|
||||
<state_interface name="position"><param name="initial_value">0.0</param></state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="joint_3">
|
||||
<command_interface name="position"><param name="min">-2.0</param><param name="max">2.0</param></command_interface>
|
||||
<state_interface name="position"><param name="initial_value">0.0</param></state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="joint_4">
|
||||
<command_interface name="position"><param name="min">-1.57</param><param name="max">1.57</param></command_interface>
|
||||
<state_interface name="position"><param name="initial_value">0.0</param></state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
</ros2_control>
|
||||
|
||||
<gazebo>
|
||||
<plugin name="gz_ros2_control::GazeboSimROS2ControlPlugin" filename="libgz_ros2_control-system.so">
|
||||
<parameters>$(find arm_simulation)/config/ros2_controllers.yaml</parameters>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
|
||||
<gazebo reference="base_link"><visual><material><ambient>0.1 0.1 0.1 1.0</ambient><diffuse>0.1 0.1 0.1 1.0</diffuse></material></visual></gazebo>
|
||||
<gazebo reference="link_1"><visual><material><ambient>0.0 0.0 0.8 1.0</ambient><diffuse>0.0 0.0 0.8 1.0</diffuse></material></visual></gazebo>
|
||||
<gazebo reference="link_2"><visual><material><ambient>0.5 0.5 0.5 1.0</ambient><diffuse>0.5 0.5 0.5 1.0</diffuse></material></visual></gazebo>
|
||||
<gazebo reference="link_3"><visual><material><ambient>0.0 0.0 0.8 1.0</ambient><diffuse>0.0 0.0 0.8 1.0</diffuse></material></visual></gazebo>
|
||||
<gazebo reference="link_4"><visual><material><ambient>0.8 0.0 0.0 1.0</ambient><diffuse>0.8 0.0 0.0 1.0</diffuse></material></visual></gazebo>
|
||||
|
||||
</robot>
|
||||
Reference in New Issue
Block a user