diff --git a/README.md b/README.md index 7503985..921b604 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,53 @@ chmod +x install.sh && . install.sh ## Running sensor server ```console -ros2 launch sas_force_sensor_bota sas_force_sensor_bota_launch.py +ros2 launch sas_force_sensor_bota force_sensor_launch.py ``` +The launch file loads the parameters from `config/config.yaml` (override with +`config_file:=/path/to/config.yaml`). The hardware-specific JSON configuration +(`configuration_file_path`) is embedded in the YAML file: `config/config.yaml` +points at `bota_binary_gen0.json` and `config/config_ethercat.yaml` points at +`ethercat_gen0.json` (for the EtherCAT hardware). + | Configurable Parameter | Meaning | |------------------------|---------| -|`topic_name`| Topic prefix, for instance, if there are multiple sensors in the same `ROS_DOMAIN`.| -|`configuration_file_path`| The path to the `json` configuration file, see examples in the `config` folder.| +| `config_file` (launch argument) | Path to the YAML configuration file. Defaults to `config/config.yaml`; use `config_ethercat.yaml` for the EtherCAT hardware. | + +## ROS 2 Nodes & Parameters + +### Node: `sas_force_sensor_bota_node` + +| Property | Value | +|---|---| +| **Executable** | `sas_force_sensor_bota_node` | +| **ROS node name** | `sas_force_sensor_bota_node` (set by the `name` launch argument of `force_sensor_launch.py`) | +| **Description** | Reads the Bota force/torque sensor, publishes the wrench on `/get/wrench`, and keeps the sensor configured/activated for the node lifetime. | + +#### Parameters + +| Parameter | Type | Mandatory / Optional | Default | Purpose | +|---|---|---|---|---| +| `topic_name` | string | Optional | `/sas_force_sensor_bota` | Topic prefix; e.g. when there are multiple sensors in the same `ROS_DOMAIN` | +| `configuration_file_path` | string | Optional | in-code default pointing at `config/bota_binary_gen0.json` in the package share directory | Path to the Bota JSON configuration file (hardware-specific). May be given as an absolute path or as a filename relative to the package `config/` directory (as in the sample YAML files) | +| `sampling_time` | double | Optional | `0.01` | Sampling period of the read loop, in seconds | + +**How mandatory/optional is determined in code:** +- **Mandatory** params are declared without a default — the node fails if they are not provided. +- **Optional** params are declared with an in-code default. + +#### Sample launch + +```console +ros2 launch sas_force_sensor_bota force_sensor_launch.py +``` + +To use the EtherCAT JSON configuration (select the EtherCAT YAML, which +embeds `configuration_file_path: ethercat_gen0.json`): + +```console +ros2 launch sas_force_sensor_bota force_sensor_launch.py config_file:=$(ros2 pkg prefix sas_force_sensor_bota --share)/config/config_ethercat.yaml +``` ## Example client diff --git a/config/config.yaml b/config/config.yaml new file mode 100644 index 0000000..3ab46d8 --- /dev/null +++ b/config/config.yaml @@ -0,0 +1,13 @@ +# Parameters for the sas_force_sensor_bota node. +# +# Loaded by force_sensor_launch.py via its `config_file` argument. The +# hardware-specific `configuration_file_path` (a JSON file in this directory) +# is embedded here; use config_ethercat.yaml for the EtherCAT hardware. + +# sas_force_sensor_bota_node +sas_force_sensor_bota_node: + ros__parameters: + topic_name: '/sas_force_sensor_bota' + # Relative to the package config/ directory; absolute paths are also accepted. + configuration_file_path: 'bota_binary_gen0.json' + sampling_time: 0.01 # 100 Hz diff --git a/config/config_ethercat.yaml b/config/config_ethercat.yaml new file mode 100644 index 0000000..d5d21ba --- /dev/null +++ b/config/config_ethercat.yaml @@ -0,0 +1,13 @@ +# Parameters for the sas_force_sensor_bota node — EtherCAT hardware variant. +# +# Loaded by force_sensor_launch.py via its `config_file` argument +# (config_file:=.../config_ethercat.yaml). The only difference from +# config.yaml is the hardware-specific `configuration_file_path`. + +# sas_force_sensor_bota_node +sas_force_sensor_bota_node: + ros__parameters: + topic_name: '/sas_force_sensor_bota' + # Relative to the package config/ directory; absolute paths are also accepted. + configuration_file_path: 'ethercat_gen0.json' + sampling_time: 0.01 # 100 Hz diff --git a/docker/compose.yml b/docker/compose.yml index 03c03d0..f4e262a 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -15,7 +15,7 @@ services: cd /root/sas_force_sensor_bota_devel && colcon build && source install/setup.bash - && timeout -s INT $$TIMEOUT ros2 launch sas_force_sensor_bota sas_force_sensor_bota_launch.py" + && timeout -s INT $$TIMEOUT ros2 launch sas_force_sensor_bota force_sensor_launch.py" sas_force_sensor_bota_client: build: diff --git a/docker/compose_ethercat.yml b/docker/compose_ethercat.yml index 8a9cae8..5fab6b1 100644 --- a/docker/compose_ethercat.yml +++ b/docker/compose_ethercat.yml @@ -15,7 +15,7 @@ services: cd /root/sas_force_sensor_bota_devel && colcon build && source install/setup.bash - && timeout -s INT $$TIMEOUT ros2 launch sas_force_sensor_bota sas_force_sensor_bota_launch.py configuration_file_path:=/root/sas_force_sensor_bota_devel/src/sas_force_sensor_bota/config/ethercat_gen0.json" + && timeout -s INT $$TIMEOUT ros2 launch sas_force_sensor_bota force_sensor_launch.py config_file:=$$(ros2 pkg prefix sas_force_sensor_bota --share)/config/config_ethercat.yaml" sas_force_sensor_bota_client: build: . diff --git a/docker/demo/compose.yml b/docker/demo/compose.yml index a807cd6..2fa3bf4 100644 --- a/docker/demo/compose.yml +++ b/docker/demo/compose.yml @@ -15,7 +15,7 @@ services: && chmod +x install.sh && . install.sh && source /opt/ros/jazzy/setup.bash - && timeout -s INT $$TIMEOUT ros2 launch sas_force_sensor_bota sas_force_sensor_bota_launch.py" + && timeout -s INT $$TIMEOUT ros2 launch sas_force_sensor_bota force_sensor_launch.py" sas_force_sensor_bota_client: image: murilomarinho/sas:jazzy diff --git a/launch/force_sensor_launch.py b/launch/force_sensor_launch.py new file mode 100644 index 0000000..84ee455 --- /dev/null +++ b/launch/force_sensor_launch.py @@ -0,0 +1,39 @@ +import os.path + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + + +def generate_launch_description(): + """Launch the Bota force sensor node. + + Parameters are loaded from a YAML configuration file. Pass a different + file with ``config_file:=/path/to/config.yaml`` (e.g. + ``config_ethercat.yaml`` for the EtherCAT hardware). The + hardware-specific ``configuration_file_path`` (a JSON file in ``config/``) + is embedded in the YAML configuration file. + """ + name = LaunchConfiguration('name') + config_file = LaunchConfiguration('config_file') + + return LaunchDescription([ + DeclareLaunchArgument( + 'name', + default_value='sas_force_sensor_bota_node' + ), + DeclareLaunchArgument( + 'config_file', + default_value=os.path.join(get_package_share_directory('sas_force_sensor_bota'), 'config', 'config.yaml') + ), + Node( + package='sas_force_sensor_bota', + executable='sas_force_sensor_bota_node', + output='screen', + emulate_tty=True, + name=name, + parameters=[config_file] + ) + ]) diff --git a/launch/sas_force_sensor_bota_launch.py b/launch/sas_force_sensor_bota_launch.py deleted file mode 100644 index 51b6dce..0000000 --- a/launch/sas_force_sensor_bota_launch.py +++ /dev/null @@ -1,38 +0,0 @@ -import pathlib -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument -from launch_ros.actions import Node -from launch.substitutions import LaunchConfiguration -from ament_index_python.packages import get_package_share_directory - - -def generate_launch_description(): - - topic_name = LaunchConfiguration('topic_name') - - configuration_file_path = LaunchConfiguration('configuration_file_path') - this_package_share_directory = get_package_share_directory('sas_force_sensor_bota') - default_configuration_file_path = str(this_package_share_directory / pathlib.Path("config") / pathlib.Path( - "../config/bota_binary_gen0.json")) - - return LaunchDescription([ - DeclareLaunchArgument( - 'topic_name', - default_value='/sas_force_sensor_bota', - ), - DeclareLaunchArgument( - 'configuration_file_path', - default_value=default_configuration_file_path - ), - Node( - package='sas_force_sensor_bota', - executable='sas_force_sensor_bota_node', - output='screen', - emulate_tty=True, - name='sas_force_sensor_bota_node', - parameters=[{ - "topic_name": topic_name, - "configuration_file_path": configuration_file_path, - }] - ) - ]) diff --git a/sas_force_sensor_bota/force_sensor_bota.py b/sas_force_sensor_bota/force_sensor_bota.py index 4109606..b2f76e7 100644 --- a/sas_force_sensor_bota/force_sensor_bota.py +++ b/sas_force_sensor_bota/force_sensor_bota.py @@ -8,6 +8,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ +import os.path import pathlib import bota_driver @@ -26,11 +27,18 @@ def __init__(self): this_package_share_directory = get_package_share_directory('sas_force_sensor_bota') default_configuration_file_path = str(this_package_share_directory / pathlib.Path("config") / pathlib.Path( - "../config/bota_binary_gen0.json")) + "bota_binary_gen0.json")) self.declare_parameter('configuration_file_path', default_configuration_file_path) configuration_file_path = self.get_parameter('configuration_file_path').get_parameter_value().string_value + # A path relative to the package config/ directory (as in the sample + # YAML configurations) is resolved against that directory. Absolute + # paths are used as given. + if not os.path.isabs(configuration_file_path): + configuration_file_path = str(this_package_share_directory / pathlib.Path("config") / pathlib.Path( + configuration_file_path)) + self.declare_parameter('sampling_time', 0.01) sampling_time = self.get_parameter('sampling_time').get_parameter_value().double_value diff --git a/setup.py b/setup.py index dac66ef..77f2e2e 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,8 @@ ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*'))), - (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.json'))) + (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.json'))), + (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.yaml'))) ], install_requires=['setuptools'], zip_safe=True,