diff --git a/.gitignore b/.gitignore
index 19b7394a..2f6c655b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
.DS_Store
.vscode/
__pycache__/
+.github/
+openarm_description/
diff --git "a/dev/\344\277\256\346\224\271\350\250\230\351\214\204.md" "b/dev/\344\277\256\346\224\271\350\250\230\351\214\204.md"
new file mode 100644
index 00000000..94801cde
--- /dev/null
+++ "b/dev/\344\277\256\346\224\271\350\250\230\351\214\204.md"
@@ -0,0 +1,59 @@
+# 註冊新硬件接口
+openarm_hardware.xml
+```
+class name="openarm_hardware/OpenArm_OYHW"
+```
+
+
+## Markdown 內容樣式範例
+
+### 標題
+```markdown
+# 一級標題
+## 二級標題
+### 三級標題
+```
+
+### 列表
+```markdown
+- 無序列表項目 1
+- 無序列表項目 2
+
+1. 有序列表項目 1
+2. 有序列表項目 2
+```
+
+### 程式碼區塊
+```markdown
+`行內程式碼`
+
+```python
+def hello():
+ print("Hello World")
+```
+```
+
+### 連結與圖片
+```markdown
+[連結文字](https://example.com)
+
+```
+
+### 表格
+```markdown
+| 欄位1 | 欄位2 | 欄位3 |
+|-------|-------|-------|
+| 內容1 | 內容2 | 內容3 |
+```
+
+### 引用
+```markdown
+> 這是一段引用文字
+```
+
+### 強調
+```markdown
+**粗體文字**
+*斜體文字*
+~~刪除線~~
+```
\ No newline at end of file
diff --git a/openarm.repos b/openarm.repos
index b3cce359..e90cbb20 100644
--- a/openarm.repos
+++ b/openarm.repos
@@ -15,5 +15,5 @@
repositories:
openarm_can:
type: git
- url: https://github.com/enactic/openarm_can.git
- version: main
\ No newline at end of file
+ url: git@github.com:openvmi/openarm_can.git
+ version: oy_openarm_can
diff --git a/openarm_bimanual_moveit_config/config/openarm_bimanual.srdf b/openarm_bimanual_moveit_config/config/openarm_bimanual.srdf
index 8c2c6e1d..632c1f58 100644
--- a/openarm_bimanual_moveit_config/config/openarm_bimanual.srdf
+++ b/openarm_bimanual_moveit_config/config/openarm_bimanual.srdf
@@ -104,8 +104,8 @@
-
-
+
+
diff --git a/openarm_bimanual_moveit_config/config/openarm_bimanual.urdf.xacro b/openarm_bimanual_moveit_config/config/openarm_bimanual.urdf.xacro
index 6f6f4951..7a621541 100644
--- a/openarm_bimanual_moveit_config/config/openarm_bimanual.urdf.xacro
+++ b/openarm_bimanual_moveit_config/config/openarm_bimanual.urdf.xacro
@@ -19,8 +19,7 @@
-
-
+
diff --git a/openarm_bimanual_moveit_config/config/ros2_controllers.yaml b/openarm_bimanual_moveit_config/config/ros2_controllers.yaml
index de64f007..f878ee15 100644
--- a/openarm_bimanual_moveit_config/config/ros2_controllers.yaml
+++ b/openarm_bimanual_moveit_config/config/ros2_controllers.yaml
@@ -44,6 +44,7 @@ left_arm_controller:
- openarm_left_joint7
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
- velocity
@@ -61,6 +62,7 @@ right_arm_controller:
- openarm_right_joint7
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
- velocity
diff --git a/openarm_bimanual_moveit_config/launch/demo.launch.py b/openarm_bimanual_moveit_config/launch/demo.launch.py
index 5ed6731c..a1ae036a 100644
--- a/openarm_bimanual_moveit_config/launch/demo.launch.py
+++ b/openarm_bimanual_moveit_config/launch/demo.launch.py
@@ -139,6 +139,41 @@ def controller_spawner(context: LaunchContext, robot_controller):
def generate_launch_description():
+ """Generate launch description for OpenARM bimanual robot with MoveIt2 configuration.
+
+ This function creates a complete launch description that sets up a dual-arm (bimanual)
+ OpenARM robot system with MoveIt2 motion planning capabilities.
+
+ The launch description includes:
+ - Robot description and state publisher nodes via OpaqueFunction
+ - Joint state broadcaster for publishing joint states
+ - Arm trajectory controllers (forward position or joint trajectory)
+ - Gripper controllers for both left and right grippers
+ - MoveIt2 move_group node for motion planning
+ - RViz2 visualization with MoveIt configuration
+
+ Launch Arguments:
+ description_package (str): Package containing robot description. Default: "openarm_description"
+ description_file (str): URDF/xacro file name. Default: "oy.urdf.xacro"
+ arm_type (str): Type of arm configuration. Default: "oy"
+ use_fake_hardware (str): Whether to use simulated hardware. Default: "false"
+ robot_controller (str): Controller type to use. Default: "joint_trajectory_controller"
+ Choices: "forward_position_controller", "joint_trajectory_controller"
+ runtime_config_package (str): Package with runtime configs. Default: "openarm_bringup"
+ arm_prefix (str): Prefix for arm links/joints. Default: ""
+ right_can_interface (str): CAN interface for right arm. Default: "can0"
+ left_can_interface (str): CAN interface for left arm. Default: "can1"
+ controllers_file (str): Controllers YAML config file. Default: "openarm_oy_bimanual_controllers.yaml"
+
+ Note:
+ OpaqueFunction is used to defer node creation until launch time, allowing
+ dynamic configuration based on launch arguments that are only resolved
+ at runtime. This is necessary when node parameters depend on substitutions
+ that cannot be evaluated during launch file parsing.
+
+ Returns:
+ LaunchDescription: Complete launch description for the bimanual robot system.
+ """
declared_arguments = [
DeclareLaunchArgument(
"description_package",
@@ -146,9 +181,9 @@ def generate_launch_description():
),
DeclareLaunchArgument(
"description_file",
- default_value="v10.urdf.xacro",
+ default_value="oy.urdf.xacro",
),
- DeclareLaunchArgument("arm_type", default_value="v10"),
+ DeclareLaunchArgument("arm_type", default_value="oy"),
DeclareLaunchArgument("use_fake_hardware", default_value="false"),
DeclareLaunchArgument(
"robot_controller",
@@ -164,7 +199,7 @@ def generate_launch_description():
DeclareLaunchArgument("left_can_interface", default_value="can1"),
DeclareLaunchArgument(
"controllers_file",
- default_value="openarm_v10_bimanual_controllers.yaml",
+ default_value="openarm_oy_bimanual_controllers.yaml",
),
]
@@ -181,7 +216,7 @@ def generate_launch_description():
controllers_file = PathJoinSubstitution(
[FindPackageShare(runtime_config_package), "config",
- "v10_controllers", controllers_file]
+ "oy_controllers", controllers_file]
)
robot_nodes_spawner_func = OpaqueFunction(
diff --git a/openarm_bringup/config/oy_controllers/openarm_oy_bimanual_controllers.yaml b/openarm_bringup/config/oy_controllers/openarm_oy_bimanual_controllers.yaml
new file mode 100644
index 00000000..ab6c8eaa
--- /dev/null
+++ b/openarm_bringup/config/oy_controllers/openarm_oy_bimanual_controllers.yaml
@@ -0,0 +1,179 @@
+# Copyright 2025 Enactic, Inc.
+# Copyright 2024 Stogl Robotics Consulting UG (haftungsbeschränkt)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+controller_manager:
+ ros__parameters:
+ update_rate: 100 # Hz
+
+ joint_state_broadcaster:
+ type: joint_state_broadcaster/JointStateBroadcaster
+
+ left_forward_position_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ left_forward_velocity_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ left_joint_trajectory_controller:
+ type: joint_trajectory_controller/JointTrajectoryController
+
+ left_gripper_controller:
+ type: position_controllers/GripperActionController
+
+ right_forward_position_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ right_forward_velocity_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ right_joint_trajectory_controller:
+ type: joint_trajectory_controller/JointTrajectoryController
+
+ right_gripper_controller:
+ type: position_controllers/GripperActionController
+
+
+left_forward_position_controller:
+ ros__parameters:
+ joints:
+ - openarm_left_joint1
+ - openarm_left_joint2
+ - openarm_left_joint3
+ - openarm_left_joint4
+ - openarm_left_joint5
+ - openarm_left_joint6
+ - openarm_left_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ state_interfaces:
+ - position
+
+left_forward_velocity_controller:
+ ros__parameters:
+ joints:
+ - openarm_left_joint1
+ - openarm_left_joint2
+ - openarm_left_joint3
+ - openarm_left_joint4
+ - openarm_left_joint5
+ - openarm_left_joint6
+ - openarm_left_joint7
+ interface_name: velocity
+ command_interfaces:
+ - velocity
+ state_interfaces:
+ - velocity
+
+left_joint_trajectory_controller:
+ ros__parameters:
+ joints:
+ - openarm_left_joint1
+ - openarm_left_joint2
+ - openarm_left_joint3
+ - openarm_left_joint4
+ - openarm_left_joint5
+ - openarm_left_joint6
+ - openarm_left_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ - velocity
+ state_interfaces:
+ - position
+ - velocity
+
+ state_publish_rate: 50.0 # Defaults to 50
+ action_monitor_rate: 50.0 # Defaults to 20
+
+ allow_partial_joints_goal: false # Defaults to false
+ constraints:
+ stopped_velocity_tolerance: 0.05 # Relaxed for real motor velocity jitter
+ goal_time: 0.0 # Defaults to 0.0 (start immediately)
+
+left_gripper_controller:
+ ros__parameters:
+ joint: openarm_left_finger_joint1
+ command_interfaces:
+ - position
+ state_interfaces:
+ - position
+
+
+right_forward_position_controller:
+ ros__parameters:
+ joints:
+ - openarm_right_joint1
+ - openarm_right_joint2
+ - openarm_right_joint3
+ - openarm_right_joint4
+ - openarm_right_joint5
+ - openarm_right_joint6
+ - openarm_right_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ state_interfaces:
+ - position
+
+right_forward_velocity_controller:
+ ros__parameters:
+ joints:
+ - openarm_right_joint1
+ - openarm_right_joint2
+ - openarm_right_joint3
+ - openarm_right_joint4
+ - openarm_right_joint5
+ - openarm_right_joint6
+ - openarm_right_joint7
+ interface_name: velocity
+ command_interfaces:
+ - velocity
+ state_interfaces:
+ - velocity
+
+right_joint_trajectory_controller:
+ ros__parameters:
+ joints:
+ - openarm_right_joint1
+ - openarm_right_joint2
+ - openarm_right_joint3
+ - openarm_right_joint4
+ - openarm_right_joint5
+ - openarm_right_joint6
+ - openarm_right_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ - velocity
+ state_interfaces:
+ - position
+ - velocity
+
+ state_publish_rate: 50.0 # Defaults to 50
+ action_monitor_rate: 50.0 # Defaults to 20
+
+ allow_partial_joints_goal: false # Defaults to false
+ constraints:
+ stopped_velocity_tolerance: 0.05 # Relaxed for real motor velocity jitter
+ goal_time: 0.0 # Defaults to 0.0 (start immediately)
+
+right_gripper_controller:
+ ros__parameters:
+ joint: openarm_right_finger_joint1
+ command_interfaces:
+ - position
+ state_interfaces:
+ - position
\ No newline at end of file
diff --git a/openarm_bringup/config/oy_controllers/openarm_oy_bimanual_controllers_namespaced.yaml b/openarm_bringup/config/oy_controllers/openarm_oy_bimanual_controllers_namespaced.yaml
new file mode 100644
index 00000000..3e2ceee7
--- /dev/null
+++ b/openarm_bringup/config/oy_controllers/openarm_oy_bimanual_controllers_namespaced.yaml
@@ -0,0 +1,140 @@
+# Copyright 2025 Enactic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+/**:
+ ros__parameters:
+ update_rate: 100 # Hz
+
+ joint_state_broadcaster:
+ type: joint_state_broadcaster/JointStateBroadcaster
+
+ left_joint_trajectory_controller:
+ type: joint_trajectory_controller/JointTrajectoryController
+
+ right_joint_trajectory_controller:
+ type: joint_trajectory_controller/JointTrajectoryController
+
+ left_forward_position_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ right_forward_position_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ left_gripper_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ right_gripper_controller:
+ type: forward_command_controller/ForwardCommandController
+
+/**/left_joint_trajectory_controller:
+ ros__parameters:
+ joints:
+ - openarm_left_joint1
+ - openarm_left_joint2
+ - openarm_left_joint3
+ - openarm_left_joint4
+ - openarm_left_joint5
+ - openarm_left_joint6
+ - openarm_left_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ - velocity
+ state_interfaces:
+ - position
+ - velocity
+ state_publish_rate: 100.0
+ action_monitor_rate: 20.0
+ allow_partial_joints_goal: false
+ open_loop_control: false
+ allow_integration_in_goal_trajectories: true
+ constraints:
+ stopped_velocity_tolerance: 0.05
+ goal_time: 0.6
+ openarm_left_joint1: {trajectory: 0.1, goal: 0.1}
+ openarm_left_joint2: {trajectory: 0.1, goal: 0.1}
+ openarm_left_joint3: {trajectory: 0.1, goal: 0.1}
+ openarm_left_joint4: {trajectory: 0.1, goal: 0.1}
+ openarm_left_joint5: {trajectory: 0.1, goal: 0.1}
+ openarm_left_joint6: {trajectory: 0.1, goal: 0.1}
+ openarm_left_joint7: {trajectory: 0.1, goal: 0.1}
+
+/**/right_joint_trajectory_controller:
+ ros__parameters:
+ joints:
+ - openarm_right_joint1
+ - openarm_right_joint2
+ - openarm_right_joint3
+ - openarm_right_joint4
+ - openarm_right_joint5
+ - openarm_right_joint6
+ - openarm_right_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ - velocity
+ state_interfaces:
+ - position
+ - velocity
+ state_publish_rate: 100.0
+ action_monitor_rate: 20.0
+ allow_partial_joints_goal: false
+ open_loop_control: false
+ allow_integration_in_goal_trajectories: true
+ constraints:
+ stopped_velocity_tolerance: 0.05
+ goal_time: 0.6
+ openarm_right_joint1: {trajectory: 0.1, goal: 0.1}
+ openarm_right_joint2: {trajectory: 0.1, goal: 0.1}
+ openarm_right_joint3: {trajectory: 0.1, goal: 0.1}
+ openarm_right_joint4: {trajectory: 0.1, goal: 0.1}
+ openarm_right_joint5: {trajectory: 0.1, goal: 0.1}
+ openarm_right_joint6: {trajectory: 0.1, goal: 0.1}
+ openarm_right_joint7: {trajectory: 0.1, goal: 0.1}
+
+/**/left_forward_position_controller:
+ ros__parameters:
+ joints:
+ - openarm_left_joint1
+ - openarm_left_joint2
+ - openarm_left_joint3
+ - openarm_left_joint4
+ - openarm_left_joint5
+ - openarm_left_joint6
+ - openarm_left_joint7
+ interface_name: position
+
+/**/right_forward_position_controller:
+ ros__parameters:
+ joints:
+ - openarm_right_joint1
+ - openarm_right_joint2
+ - openarm_right_joint3
+ - openarm_right_joint4
+ - openarm_right_joint5
+ - openarm_right_joint6
+ - openarm_right_joint7
+ interface_name: position
+
+/**/left_gripper_controller:
+ ros__parameters:
+ joints:
+ - openarm_left_finger_joint1
+ interface_name: position
+
+/**/right_gripper_controller:
+ ros__parameters:
+ joints:
+ - openarm_right_finger_joint1
+ interface_name: position
diff --git a/openarm_bringup/config/oy_controllers/openarm_oy_controllers.yaml b/openarm_bringup/config/oy_controllers/openarm_oy_controllers.yaml
new file mode 100644
index 00000000..b696e647
--- /dev/null
+++ b/openarm_bringup/config/oy_controllers/openarm_oy_controllers.yaml
@@ -0,0 +1,102 @@
+# Copyright 2025 Enactic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Controller configuration for OpenArm with OY Motors
+
+controller_manager:
+ ros__parameters:
+ update_rate: 100 # Hz
+
+ joint_state_broadcaster:
+ type: joint_state_broadcaster/JointStateBroadcaster
+
+ forward_position_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ forward_velocity_controller:
+ type: forward_command_controller/ForwardCommandController
+
+ joint_trajectory_controller:
+ type: joint_trajectory_controller/JointTrajectoryController
+
+ gripper_controller:
+ type: position_controllers/GripperActionController
+
+forward_position_controller:
+ ros__parameters:
+ joints:
+ - openarm_joint1
+ - openarm_joint2
+ - openarm_joint3
+ - openarm_joint4
+ - openarm_joint5
+ - openarm_joint6
+ - openarm_joint7
+ interface_name: position
+ command_interfaces:
+ - position
+ state_interfaces:
+ - position
+
+
+forward_velocity_controller:
+ ros__parameters:
+ joints:
+ - openarm_joint1
+ - openarm_joint2
+ - openarm_joint3
+ - openarm_joint4
+ - openarm_joint5
+ - openarm_joint6
+ - openarm_joint7
+ interface_name: velocity
+ command_interfaces:
+ - velocity
+ state_interfaces:
+ - velocity
+
+joint_trajectory_controller:
+ ros__parameters:
+ joints:
+ - openarm_joint1
+ - openarm_joint2
+ - openarm_joint3
+ - openarm_joint4
+ - openarm_joint5
+ - openarm_joint6
+ - openarm_joint7
+
+ command_interfaces:
+ - position
+ - velocity
+ state_interfaces:
+ - position
+ - velocity
+
+ state_publish_rate: 50.0 # Defaults to 50
+ action_monitor_rate: 50.0 # Defaults to 20
+
+ allow_partial_joints_goal: false # Defaults to false
+ constraints:
+ stopped_velocity_tolerance: 0.05 # Relaxed for real motor velocity jitter
+ goal_time: 0.0 # Defaults to 0.0 (start immediately)
+
+
+gripper_controller:
+ ros__parameters:
+ joint: openarm_finger_joint1
+ command_interfaces:
+ - position
+ state_interfaces:
+ - position
diff --git a/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers.yaml b/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers.yaml
index 8a386eec..ab6c8eaa 100644
--- a/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers.yaml
+++ b/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers.yaml
@@ -90,15 +90,17 @@ left_joint_trajectory_controller:
interface_name: position
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
+ - velocity
state_publish_rate: 50.0 # Defaults to 50
action_monitor_rate: 50.0 # Defaults to 20
allow_partial_joints_goal: false # Defaults to false
constraints:
- stopped_velocity_tolerance: 0.01 # Defaults to 0.01
+ stopped_velocity_tolerance: 0.05 # Relaxed for real motor velocity jitter
goal_time: 0.0 # Defaults to 0.0 (start immediately)
left_gripper_controller:
@@ -155,15 +157,17 @@ right_joint_trajectory_controller:
interface_name: position
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
+ - velocity
state_publish_rate: 50.0 # Defaults to 50
action_monitor_rate: 50.0 # Defaults to 20
allow_partial_joints_goal: false # Defaults to false
constraints:
- stopped_velocity_tolerance: 0.01 # Defaults to 0.01
+ stopped_velocity_tolerance: 0.05 # Relaxed for real motor velocity jitter
goal_time: 0.0 # Defaults to 0.0 (start immediately)
right_gripper_controller:
diff --git a/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers_namespaced.yaml b/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers_namespaced.yaml
index ca0b9721..3e2ceee7 100644
--- a/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers_namespaced.yaml
+++ b/openarm_bringup/config/v10_controllers/openarm_v10_bimanual_controllers_namespaced.yaml
@@ -50,6 +50,7 @@
interface_name: position
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
- velocity
@@ -59,7 +60,7 @@
open_loop_control: false
allow_integration_in_goal_trajectories: true
constraints:
- stopped_velocity_tolerance: 0.01
+ stopped_velocity_tolerance: 0.05
goal_time: 0.6
openarm_left_joint1: {trajectory: 0.1, goal: 0.1}
openarm_left_joint2: {trajectory: 0.1, goal: 0.1}
@@ -82,6 +83,7 @@
interface_name: position
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
- velocity
@@ -91,7 +93,7 @@
open_loop_control: false
allow_integration_in_goal_trajectories: true
constraints:
- stopped_velocity_tolerance: 0.01
+ stopped_velocity_tolerance: 0.05
goal_time: 0.6
openarm_right_joint1: {trajectory: 0.1, goal: 0.1}
openarm_right_joint2: {trajectory: 0.1, goal: 0.1}
diff --git a/openarm_bringup/config/v10_controllers/openarm_v10_controllers.yaml b/openarm_bringup/config/v10_controllers/openarm_v10_controllers.yaml
index f2f3a03c..89646639 100644
--- a/openarm_bringup/config/v10_controllers/openarm_v10_controllers.yaml
+++ b/openarm_bringup/config/v10_controllers/openarm_v10_controllers.yaml
@@ -78,15 +78,17 @@ joint_trajectory_controller:
command_interfaces:
- position
+ - velocity
state_interfaces:
- position
+ - velocity
state_publish_rate: 50.0 # Defaults to 50
action_monitor_rate: 50.0 # Defaults to 20
allow_partial_joints_goal: false # Defaults to false
constraints:
- stopped_velocity_tolerance: 0.01 # Defaults to 0.01
+ stopped_velocity_tolerance: 0.05 # Relaxed for real motor velocity jitter
goal_time: 0.0 # Defaults to 0.0 (start immediately)
diff --git a/openarm_bringup/launch/openarm.bimanual.launch.py b/openarm_bringup/launch/openarm.bimanual.launch.py
index 99250a5c..221f68a8 100644
--- a/openarm_bringup/launch/openarm.bimanual.launch.py
+++ b/openarm_bringup/launch/openarm.bimanual.launch.py
@@ -64,6 +64,8 @@ def generate_robot_description(context: LaunchContext, description_package, desc
"left_can_interface": left_can_interface_str,
}
).toprettyxml(indent=" ")
+ # 輸出robot_description
+ # print(robot_description)
return robot_description
@@ -82,7 +84,7 @@ def robot_nodes_spawner(context: LaunchContext, description_package, description
if namespace:
controllers_file_str = controllers_file_str.replace(
- "openarm_v10_bimanual_controllers.yaml", "openarm_v10_bimanual_controllers_namespaced.yaml"
+ "openarm_oy_bimanual_controllers.yaml", "openarm_oy_bimanual_controllers_namespaced.yaml"
)
robot_state_pub_node = Node(
package="robot_state_publisher",
@@ -144,13 +146,13 @@ def generate_launch_description():
),
DeclareLaunchArgument(
"description_file",
- default_value="v10.urdf.xacro",
+ default_value="oy.urdf.xacro",
description="URDF/XACRO description file with the robot.",
),
DeclareLaunchArgument(
"arm_type",
- default_value="v10",
- description="Type of arm (e.g., v10).",
+ default_value="oy",
+ description="Type of arm (e.g., oy, v10).",
),
DeclareLaunchArgument(
"use_fake_hardware",
@@ -186,7 +188,7 @@ def generate_launch_description():
),
DeclareLaunchArgument(
"controllers_file",
- default_value="openarm_v10_bimanual_controllers.yaml",
+ default_value="openarm_oy_bimanual_controllers.yaml",
description="Controllers file(s) to use. Can be a single file or comma-separated list of files.",
),
]
@@ -205,7 +207,7 @@ def generate_launch_description():
controllers_file = PathJoinSubstitution(
[FindPackageShare(runtime_config_package), "config",
- "v10_controllers", controllers_file]
+ "oy_controllers", controllers_file]
)
robot_nodes_spawner_func = OpaqueFunction(
diff --git a/openarm_bringup/launch/openarm.launch.py b/openarm_bringup/launch/openarm.launch.py
index 847bbd0d..c9df16eb 100644
--- a/openarm_bringup/launch/openarm.launch.py
+++ b/openarm_bringup/launch/openarm.launch.py
@@ -90,7 +90,8 @@ def robot_nodes_spawner(context: LaunchContext, description_package, description
package="controller_manager",
executable="ros2_control_node",
output="both",
- parameters=[robot_description_param, controllers_file_str],
+ parameters=[controllers_file_str],
+ remappings=[("~/robot_description", "/robot_description")],
)
return [robot_state_pub_node, control_node]
@@ -108,13 +109,13 @@ def generate_launch_description():
),
DeclareLaunchArgument(
"description_file",
- default_value="v10.urdf.xacro",
+ default_value="oy.urdf.xacro",
description="URDF/XACRO description file with the robot.",
),
DeclareLaunchArgument(
"arm_type",
- default_value="v10",
- description="Type of arm (e.g., v10).",
+ default_value="oy",
+ description="Type of arm (e.g., oy, v10).",
),
DeclareLaunchArgument(
"use_fake_hardware",
@@ -145,7 +146,7 @@ def generate_launch_description():
),
DeclareLaunchArgument(
"controllers_file",
- default_value="openarm_v10_controllers.yaml",
+ default_value="openarm_oy_controllers.yaml",
description="Controllers file(s) to use. Can be a single file or comma-separated list of files.",
),
]
@@ -163,7 +164,7 @@ def generate_launch_description():
# Configuration file paths
controllers_file = PathJoinSubstitution(
[FindPackageShare(runtime_config_package), "config",
- "v10_controllers", controllers_file]
+ "oy_controllers", controllers_file]
)
# Robot nodes spawner (both state publisher and control)
@@ -198,13 +199,13 @@ def generate_launch_description():
robot_controller_spawner = Node(
package="controller_manager",
executable="spawner",
- arguments=[robot_controller, "-c", "/controller_manager"],
+ arguments=[robot_controller, "--controller-manager", "/controller_manager"],
)
gripper_controller_spawner = Node(
package="controller_manager",
executable="spawner",
- arguments=["gripper_controller", "-c", "/controller_manager"],
+ arguments=["gripper_controller", "--controller-manager", "/controller_manager"],
)
# Timing and sequencing
@@ -213,13 +214,17 @@ def generate_launch_description():
actions=[joint_state_broadcaster_spawner],
)
- delayed_robot_controller = TimerAction(
- period=1.0,
- actions=[robot_controller_spawner],
+ delayed_robot_controller = RegisterEventHandler(
+ OnProcessExit(
+ target_action=joint_state_broadcaster_spawner,
+ on_exit=[robot_controller_spawner],
+ )
)
- delayed_gripper_controller = TimerAction(
- period=1.0,
- actions=[gripper_controller_spawner],
+ delayed_gripper_controller = RegisterEventHandler(
+ OnProcessExit(
+ target_action=robot_controller_spawner,
+ on_exit=[gripper_controller_spawner],
+ )
)
return LaunchDescription(
diff --git a/openarm_hardware/CMakeLists.txt b/openarm_hardware/CMakeLists.txt
index 1ed46920..e063c9db 100644
--- a/openarm_hardware/CMakeLists.txt
+++ b/openarm_hardware/CMakeLists.txt
@@ -30,7 +30,8 @@ find_package(rclcpp_lifecycle REQUIRED)
find_package(OpenArmCAN REQUIRED)
add_library(${PROJECT_NAME} SHARED
- src/v10_simple_hardware.cpp
+ # src/v10_simple_hardware.cpp
+ src/oy_hardware.cpp
)
target_include_directories(
diff --git a/openarm_hardware/include/openarm_hardware/oy_hardware.hpp b/openarm_hardware/include/openarm_hardware/oy_hardware.hpp
new file mode 100644
index 00000000..342a35f1
--- /dev/null
+++ b/openarm_hardware/include/openarm_hardware/oy_hardware.hpp
@@ -0,0 +1,155 @@
+// Copyright 2025 Enactic, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "hardware_interface/handle.hpp"
+#include "hardware_interface/hardware_info.hpp"
+#include "hardware_interface/system_interface.hpp"
+#include "hardware_interface/types/hardware_interface_return_values.hpp"
+#include "openarm_hardware/visibility_control.h"
+#include "rclcpp/macros.hpp"
+#include "rclcpp_lifecycle/state.hpp"
+
+namespace openarm_hardware {
+
+/**
+ * @brief OpenArm Hardware Interface for OY Motors
+ *
+ * This hardware interface uses the OpenArm CAN API with OY motors,
+ * following the pattern from full_arm.cpp example.
+ * Configurable for different arm configurations via hardware parameters.
+ */
+class OpenArm_oyHW : public hardware_interface::SystemInterface {
+ public:
+ OpenArm_oyHW();
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ hardware_interface::CallbackReturn on_init(
+ const hardware_interface::HardwareInfo& info) override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ hardware_interface::CallbackReturn on_configure(
+ const rclcpp_lifecycle::State& previous_state) override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ std::vector export_state_interfaces()
+ override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ std::vector export_command_interfaces()
+ override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ hardware_interface::CallbackReturn on_activate(
+ const rclcpp_lifecycle::State& previous_state) override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ hardware_interface::CallbackReturn on_deactivate(
+ const rclcpp_lifecycle::State& previous_state) override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ hardware_interface::return_type read(const rclcpp::Time& time,
+ const rclcpp::Duration& period) override;
+
+ TEMPLATES__ROS2_CONTROL__VISIBILITY_PUBLIC
+ hardware_interface::return_type write(
+ const rclcpp::Time& time, const rclcpp::Duration& period) override;
+
+ private:
+ // default configuration
+ static constexpr size_t ARM_DOF = 7;
+ static constexpr bool ENABLE_GRIPPER = true;
+
+ // Default OY motor configuration for V10
+ // Motor types: GIM8115_9p for large joints, GIM4310_40/GIM4315_8 for small joints
+ const std::vector DEFAULT_MOTOR_TYPES = {
+ openarm::oy_motor::MotorType::GIM8115_9p, // Joint 1
+ openarm::oy_motor::MotorType::GIM8115_9p, // Joint 2
+ openarm::oy_motor::MotorType::GIM4310_40, // Joint 3
+ openarm::oy_motor::MotorType::GIM4310_40, // Joint 4
+ openarm::oy_motor::MotorType::GIM4315_8, // Joint 5
+ openarm::oy_motor::MotorType::GIM4315_8, // Joint 6
+ openarm::oy_motor::MotorType::GIM4315_8 // Joint 7
+ };
+
+ const std::vector DEFAULT_SEND_CAN_IDS = {0x101, 0x102, 0x103, 0x104,
+ 0x105, 0x106, 0x107};
+ const std::vector DEFAULT_RECV_CAN_IDS = {0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07};
+
+ const openarm::oy_motor::MotorType DEFAULT_GRIPPER_MOTOR_TYPE =
+ openarm::oy_motor::MotorType::GIM4315_8;
+ const uint32_t DEFAULT_GRIPPER_SEND_CAN_ID = 0x108;
+ const uint32_t DEFAULT_GRIPPER_RECV_CAN_ID = 0x08;
+
+ // Default gains for OY motors (may need tuning)
+ // Order: Joint 1-7 + Gripper
+ // 帶載機械臂優化:降低 KP 避免過衝,適當提高 KD 增加阻尼抑制震盪
+ const std::vector DEFAULT_KP = {40.0, 40.0, 15.0, 15.0,
+ 2.0, 2.0, 2.0, 2.0};
+ const std::vector DEFAULT_KD = {2.0, 2.0, 1.5, 1.2,
+ 0.2, 0.2, 0.2, 0.15};
+
+ // Default gains
+// const std::vector DEFAULT_KP = {20.0, 20.0, 20.0, 20.0,
+// 5.0, 5.0, 5.0, 0.5};
+// const std::vector DEFAULT_KD = {2.75, 2.5, 0.7, 0.4,
+// 0.7, 0.6, 0.5, 0.1};
+
+ const double GRIPPER_JOINT_0_POSITION = 0.044;
+ const double GRIPPER_JOINT_1_POSITION = 0.0;
+ const double GRIPPER_MOTOR_0_RADIANS = 0.0;
+ const double GRIPPER_MOTOR_1_RADIANS = -1.0472;
+ const double GRIPPER_DEFAULT_KP = 5.0;
+ const double GRIPPER_DEFAULT_KD = 0.1;
+
+ // Configuration
+ std::string can_interface_;
+ std::string arm_prefix_;
+ bool hand_;
+ bool can_fd_;
+
+ // OpenArm instance
+ std::unique_ptr openarm_;
+
+ // Generated joint names for this arm instance
+ std::vector joint_names_;
+
+ // ROS2 control state and command vectors
+ std::vector pos_commands_;
+ std::vector vel_commands_;
+ std::vector tau_commands_;
+ std::vector pos_states_;
+ std::vector vel_states_;
+ std::vector tau_states_;
+
+ // Helper methods
+ void return_to_zero();
+ bool parse_config(const hardware_interface::HardwareInfo& info);
+ void generate_joint_names();
+
+ // Gripper mapping functions
+ double joint_to_motor_radians(double joint_value);
+ double motor_radians_to_joint(double motor_radians);
+};
+
+} // namespace openarm_hardware
diff --git a/openarm_hardware/openarm_hardware.xml b/openarm_hardware/openarm_hardware.xml
index fd4fee92..8e4e3f24 100644
--- a/openarm_hardware/openarm_hardware.xml
+++ b/openarm_hardware/openarm_hardware.xml
@@ -15,11 +15,19 @@
-->
-
- ros2_control hardware interface for OpenArm V10.
+ ros2_control hardware interface for OpenArm V10 (Damiao Motors).
+
+ -->
+
+
+ ros2_control hardware interface for OpenArm with OY Motors.
+ Configurable arm DOF via hardware parameters.
diff --git a/openarm_hardware/src/oy_hardware.cpp b/openarm_hardware/src/oy_hardware.cpp
new file mode 100644
index 00000000..1efab5d9
--- /dev/null
+++ b/openarm_hardware/src/oy_hardware.cpp
@@ -0,0 +1,351 @@
+// Copyright 2025 Enactic, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "openarm_hardware/oy_hardware.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "hardware_interface/types/hardware_interface_type_values.hpp"
+#include "rclcpp/logging.hpp"
+#include "rclcpp/rclcpp.hpp"
+
+namespace openarm_hardware {
+
+OpenArm_oyHW::OpenArm_oyHW() = default;
+
+bool OpenArm_oyHW::parse_config(const hardware_interface::HardwareInfo& info) {
+ // Parse CAN interface (default: can0)
+ auto it = info.hardware_parameters.find("can_interface");
+ can_interface_ = (it != info.hardware_parameters.end()) ? it->second : "can0";
+
+ // Parse arm prefix (default: empty for single arm, "left_" or "right_" for
+ // bimanual)
+ it = info.hardware_parameters.find("arm_prefix");
+ arm_prefix_ = (it != info.hardware_parameters.end()) ? it->second : "";
+
+ // Parse gripper enable (default: true)
+ it = info.hardware_parameters.find("hand");
+ if (it == info.hardware_parameters.end()) {
+ hand_ = true; // Default to true
+ } else {
+ // Handle both "true"/"True" and "false"/"False"
+ std::string value = it->second;
+ std::transform(value.begin(), value.end(), value.begin(), ::tolower);
+ hand_ = (value == "true");
+ }
+
+ // Parse CAN-FD enable (default: true)
+ it = info.hardware_parameters.find("can_fd");
+ if (it == info.hardware_parameters.end()) {
+ can_fd_ = true; // Default to true
+ } else {
+ // Handle both "true"/"True" and "false"/"False"
+ std::string value = it->second;
+ std::transform(value.begin(), value.end(), value.begin(), ::tolower);
+ can_fd_ = (value == "true");
+ }
+
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "Configuration: CAN=%s, arm_prefix=%s, arm_dof=%zu, hand=%s, can_fd=%s",
+ can_interface_.c_str(), arm_prefix_.c_str(), ARM_DOF,
+ hand_ ? "enabled" : "disabled", can_fd_ ? "enabled" : "disabled");
+ return true;
+}
+
+void OpenArm_oyHW::generate_joint_names() {
+ joint_names_.clear();
+ // TODO: read from urdf properly and sort in the future.
+ // Currently, the joint names are hardcoded for order consistency to align
+ // with hardware. Generate arm joint names: openarm_{arm_prefix}joint{N}
+ for (size_t i = 1; i <= ARM_DOF; ++i) {
+ std::string joint_name =
+ "openarm_" + arm_prefix_ + "joint" + std::to_string(i);
+ joint_names_.push_back(joint_name);
+ }
+
+ // Generate gripper joint name if enabled
+ if (hand_) {
+ std::string gripper_joint_name = "openarm_" + arm_prefix_ + "finger_joint1";
+ joint_names_.push_back(gripper_joint_name);
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"), "Added gripper joint: %s",
+ gripper_joint_name.c_str());
+ } else {
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "Gripper joint NOT added because hand_=false");
+ }
+
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "Generated %zu joint names for arm prefix '%s'",
+ joint_names_.size(), arm_prefix_.c_str());
+}
+
+hardware_interface::CallbackReturn OpenArm_oyHW::on_init(
+ const hardware_interface::HardwareInfo& info) {
+ if (hardware_interface::SystemInterface::on_init(info) !=
+ CallbackReturn::SUCCESS) {
+ return CallbackReturn::ERROR;
+ }
+ // Parse configuration
+ if (!parse_config(info)) {
+ return CallbackReturn::ERROR;
+ }
+
+ // Generate joint names based on arm prefix
+ generate_joint_names();
+
+ // Validate joint count (arm joints + optional gripper)
+ size_t expected_joints = ARM_DOF + (hand_ ? 1 : 0);
+ if (joint_names_.size() != expected_joints) {
+ RCLCPP_ERROR(rclcpp::get_logger("OpenArm_oyHW"),
+ "Generated %zu joint names, expected %zu", joint_names_.size(),
+ expected_joints);
+ return CallbackReturn::ERROR;
+ }
+
+ // Initialize OpenArm with configurable CAN-FD setting
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "Initializing OpenArm (OY Motor) on %s with CAN-FD %s...",
+ can_interface_.c_str(), can_fd_ ? "enabled" : "disabled");
+ openarm_ =
+ std::make_unique(can_interface_, can_fd_);
+
+ // Initialize arm motors with defaults (OY motors)
+ // Use only the required number of motors based on ARM_DOF
+ std::vector motor_types(
+ DEFAULT_MOTOR_TYPES.begin(),
+ DEFAULT_MOTOR_TYPES.begin() + std::min(ARM_DOF, DEFAULT_MOTOR_TYPES.size()));
+ std::vector send_ids(
+ DEFAULT_SEND_CAN_IDS.begin(),
+ DEFAULT_SEND_CAN_IDS.begin() + std::min(ARM_DOF, DEFAULT_SEND_CAN_IDS.size()));
+ std::vector recv_ids(
+ DEFAULT_RECV_CAN_IDS.begin(),
+ DEFAULT_RECV_CAN_IDS.begin() + std::min(ARM_DOF, DEFAULT_RECV_CAN_IDS.size()));
+
+ openarm_->init_arm_motors(motor_types, send_ids, recv_ids);
+
+ // Initialize gripper if enabled
+ if (hand_) {
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"), "Initializing gripper...");
+ openarm_->init_gripper_motor(DEFAULT_GRIPPER_MOTOR_TYPE,
+ DEFAULT_GRIPPER_SEND_CAN_ID,
+ DEFAULT_GRIPPER_RECV_CAN_ID);
+ }
+
+ // Initialize state and command vectors based on generated joint count
+ const size_t total_joints = joint_names_.size();
+ pos_commands_.resize(total_joints, 0.0);
+ vel_commands_.resize(total_joints, 0.0);
+ tau_commands_.resize(total_joints, 0.0);
+ pos_states_.resize(total_joints, 0.0);
+ vel_states_.resize(total_joints, 0.0);
+ tau_states_.resize(total_joints, 0.0);
+
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "OpenArm OY Motor HW initialized successfully with %zu DOF",
+ ARM_DOF + (hand_ ? 1 : 0));
+
+ return CallbackReturn::SUCCESS;
+}
+
+hardware_interface::CallbackReturn OpenArm_oyHW::on_configure(
+ const rclcpp_lifecycle::State& /*previous_state*/) {
+ // Refresh motor states during configuration
+ openarm_->refresh_all();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ openarm_->recv_all();
+
+ return CallbackReturn::SUCCESS;
+}
+
+std::vector
+OpenArm_oyHW::export_state_interfaces() {
+ std::vector state_interfaces;
+ for (size_t i = 0; i < joint_names_.size(); ++i) {
+ state_interfaces.emplace_back(hardware_interface::StateInterface(
+ joint_names_[i], hardware_interface::HW_IF_POSITION, &pos_states_[i]));
+ state_interfaces.emplace_back(hardware_interface::StateInterface(
+ joint_names_[i], hardware_interface::HW_IF_VELOCITY, &vel_states_[i]));
+ state_interfaces.emplace_back(hardware_interface::StateInterface(
+ joint_names_[i], hardware_interface::HW_IF_EFFORT, &tau_states_[i]));
+ }
+
+ return state_interfaces;
+}
+
+std::vector
+OpenArm_oyHW::export_command_interfaces() {
+ std::vector command_interfaces;
+ // TODO: consider exposing only needed interfaces to avoid undefined behavior.
+ for (size_t i = 0; i < joint_names_.size(); ++i) {
+ command_interfaces.emplace_back(hardware_interface::CommandInterface(
+ joint_names_[i], hardware_interface::HW_IF_POSITION,
+ &pos_commands_[i]));
+ command_interfaces.emplace_back(hardware_interface::CommandInterface(
+ joint_names_[i], hardware_interface::HW_IF_VELOCITY,
+ &vel_commands_[i]));
+ command_interfaces.emplace_back(hardware_interface::CommandInterface(
+ joint_names_[i], hardware_interface::HW_IF_EFFORT, &tau_commands_[i]));
+ }
+
+ return command_interfaces;
+}
+
+hardware_interface::CallbackReturn OpenArm_oyHW::on_activate(
+ const rclcpp_lifecycle::State& /*previous_state*/) {
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"), "Activating OpenArm OY Motor...");
+
+ // Enable all motors
+ openarm_->enable_all();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ openarm_->recv_all();
+
+ // Return to zero position
+ return_to_zero();
+
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"), "OpenArm OY Motor activated");
+ return CallbackReturn::SUCCESS;
+}
+
+hardware_interface::CallbackReturn OpenArm_oyHW::on_deactivate(
+ const rclcpp_lifecycle::State& /*previous_state*/) {
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "Deactivating OpenArm OY Motor...");
+
+ // Disable all motors
+ openarm_->disable_all();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ openarm_->recv_all();
+
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"), "OpenArm OY Motor deactivated");
+ return CallbackReturn::SUCCESS;
+}
+
+hardware_interface::return_type OpenArm_oyHW::read(
+ const rclcpp::Time& /*time*/, const rclcpp::Duration& /*period*/) {
+ // Refresh and receive all motor states
+ openarm_->refresh_all();
+ openarm_->recv_all();
+
+ // Read arm joint states
+ const auto& arm_motors = openarm_->get_arm().get_motors();
+ for (size_t i = 0; i < ARM_DOF && i < arm_motors.size(); ++i) {
+ pos_states_[i] = arm_motors[i].get_position();
+ vel_states_[i] = arm_motors[i].get_velocity();
+ tau_states_[i] = arm_motors[i].get_torque();
+ //print joint name and states for debugging
+ // RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"), "Joint %s: pos=%.3f, vel=%.3f, tau=%.3f",
+ // joint_names_[i].c_str(),
+ // pos_states_[i],
+ // vel_states_[i],
+ // tau_states_[i]);
+ }
+
+ // Read gripper state if enabled
+ if (hand_ && joint_names_.size() > ARM_DOF) {
+ const auto& gripper_motors = openarm_->get_gripper().get_motors();
+ if (!gripper_motors.empty()) {
+ // TODO the mappings are approximates
+ // Convert motor position (radians) to joint value (0-0.044m)
+ double motor_pos = gripper_motors[0].get_position();
+ pos_states_[ARM_DOF] = motor_radians_to_joint(motor_pos);
+
+ //Velocity and torque mapping
+ vel_states_[ARM_DOF] = gripper_motors[0].get_velocity();
+ tau_states_[ARM_DOF] = gripper_motors[0].get_torque();
+ }
+ }
+
+ return hardware_interface::return_type::OK;
+}
+
+hardware_interface::return_type OpenArm_oyHW::write(
+ const rclcpp::Time& /*time*/, const rclcpp::Duration& /*period*/) {
+ // Control arm motors with MIT control
+ // OY motor MITParam order: {q, dq, kp, kd, tau}
+ std::vector arm_params;
+ for (size_t i = 0; i < ARM_DOF; ++i) {
+ double kp = (i < DEFAULT_KP.size()) ? DEFAULT_KP[i] : DEFAULT_KP.back();
+ double kd = (i < DEFAULT_KD.size()) ? DEFAULT_KD[i] : DEFAULT_KD.back();
+ //print kp kd pos vel tau for debugging
+ // if( i == 0 && pos_commands_[i] != 0.0) { // Only print for the first joint if there's a command
+ // RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ // "Commanding joint %zu: pos_cmd=%.4f, vel_cmd=%.4f, kp=%.2f, kd=%.2f, tau_cmd=%.4f",
+ // i, pos_commands_[i], vel_commands_[i], kp, kd, tau_commands_[i]);
+ // }
+ arm_params.push_back({pos_commands_[i], vel_commands_[i],
+ kp, kd, tau_commands_[i]});
+ }
+
+ openarm_->get_arm().oy_mit_control_all(arm_params);
+
+ // Control gripper if enabled
+ if (hand_ && joint_names_.size() > ARM_DOF) {
+ // TODO the true mappings are unimplemented.
+ double motor_command = joint_to_motor_radians(pos_commands_[ARM_DOF]);
+ // OY motor MITParam order: {q, dq, kp, kd, tau}
+ openarm_->get_gripper().oy_mit_control_all(
+ {{motor_command, 0.0, GRIPPER_DEFAULT_KP, GRIPPER_DEFAULT_KD, 0.0}});
+ }
+ openarm_->recv_all(1000);
+ return hardware_interface::return_type::OK;
+}
+
+void OpenArm_oyHW::return_to_zero() {
+ RCLCPP_INFO(rclcpp::get_logger("OpenArm_oyHW"),
+ "Returning to zero position...");
+
+ // Return arm to zero with MIT control
+ // OY motor MITParam order: {q, dq, kp, kd, tau}
+ std::vector arm_params;
+ for (size_t i = 0; i < ARM_DOF; ++i) {
+ double kp = (i < DEFAULT_KP.size()) ? DEFAULT_KP[i] : DEFAULT_KP.back();
+ double kd = (i < DEFAULT_KD.size()) ? DEFAULT_KD[i] : DEFAULT_KD.back();
+ arm_params.push_back({0.0, 0.0, kp, kd, 0.0});
+ }
+ openarm_->get_arm().oy_mit_control_all(arm_params);
+
+ // Return gripper to zero if enabled
+ if (hand_) {
+ // OY motor MITParam order: {q, dq, kp, kd, tau}
+ openarm_->get_gripper().oy_mit_control_all(
+ {{GRIPPER_JOINT_0_POSITION, 0.0, GRIPPER_DEFAULT_KP, GRIPPER_DEFAULT_KD, 0.0}});
+ }
+ std::this_thread::sleep_for(std::chrono::microseconds(1000));
+ openarm_->recv_all();
+}
+
+// Gripper mapping helper functions
+double OpenArm_oyHW::joint_to_motor_radians(double joint_value) {
+ // Joint 0=closed -> motor 0 rad, Joint 0.044=open -> motor -1.0472 rad
+ return (joint_value / GRIPPER_JOINT_0_POSITION) *
+ GRIPPER_MOTOR_1_RADIANS; // Scale from 0-0.044 to 0 to -1.0472
+}
+
+double OpenArm_oyHW::motor_radians_to_joint(double motor_radians) {
+ // Motor 0 rad=closed -> joint 0, Motor -1.0472 rad=open -> joint 0.044
+ return GRIPPER_JOINT_0_POSITION *
+ (motor_radians /
+ GRIPPER_MOTOR_1_RADIANS); // Scale from 0 to -1.0472 to 0-0.044
+}
+
+} // namespace openarm_hardware
+
+#include "pluginlib/class_list_macros.hpp"
+
+PLUGINLIB_EXPORT_CLASS(openarm_hardware::OpenArm_oyHW,
+ hardware_interface::SystemInterface)