Skip to content

Commit

Permalink
Enable generate_parameter_module through ament_cmake_python (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
pac48 authored Jan 12, 2024
1 parent 6e20e3e commit 55118ab
Show file tree
Hide file tree
Showing 11 changed files with 761 additions and 0 deletions.
3 changes: 3 additions & 0 deletions example_cmake_python/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package cmake_generate_parameter_module_example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15 changes: 15 additions & 0 deletions example_cmake_python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.16)
project(cmake_generate_parameter_module_example)

find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(generate_parameter_library REQUIRED)

generate_parameter_module(admittance_parameters
cmake_generate_parameter_module_example/parameters.yaml
cmake_generate_parameter_module_example.custom_validation
)

ament_python_install_package(${PROJECT_NAME})

ament_package()
156 changes: 156 additions & 0 deletions example_cmake_python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Example:

## Build the node

```
mkdir -p colcon_ws/src
cd colcon_ws/src
git clone https://github.com/picknikrobotics/generate_parameter_library.git
cd ..
colcon build
```

## Run the Python node

```
source install/setup.bash
python3 src/generate_parameter_library/example_cmake_python/cmake_generate_parameter_module_example/minimal_publisher.py --ros-args --params-file src/generate_parameter_library/example_python/config/implementation.yaml
```

You should see an output like this:
`[INFO] [1656018676.015816509] [admittance_controller]: Initial control frame parameter is: 'ee_link'`


## ROS 2 CLI

Run the following:

`ros2 param list`

You should see:

```
/admittance_controller:
admittance.damping_ratio
admittance.mass
admittance.selected_axes
admittance.stiffness
chainable_command_interfaces
command_interfaces
control.frame.external
control.frame.id
enable_parameter_update_without_reactivation
fixed_array
fixed_string
fixed_string_no_default
fixed_world_frame.frame.external
fixed_world_frame.frame.id
ft_sensor.filter_coefficient
ft_sensor.frame.external
ft_sensor.frame.id
ft_sensor.name
gravity_compensation.CoG.force
gravity_compensation.CoG.pos
gravity_compensation.frame.external
gravity_compensation.frame.id
interpolation_mode
joints
kinematics.alpha
kinematics.base
kinematics.group_name
kinematics.plugin_name
kinematics.plugin_package
kinematics.tip
one_number
pid.elbow_joint.d
pid.elbow_joint.i
pid.elbow_joint.p
pid.rate
pid.shoulder_lift_joint.d
pid.shoulder_lift_joint.i
pid.shoulder_lift_joint.p
pid.shoulder_pan_joint.d
pid.shoulder_pan_joint.i
pid.shoulder_pan_joint.p
pid.wrist_1_joint.d
pid.wrist_1_joint.i
pid.wrist_1_joint.p
pid.wrist_2_joint.d
pid.wrist_2_joint.i
pid.wrist_2_joint.p
pid.wrist_3_joint.d
pid.wrist_3_joint.i
pid.wrist_3_joint.p
qos_overrides./parameter_events.publisher.depth
qos_overrides./parameter_events.publisher.durability
qos_overrides./parameter_events.publisher.history
qos_overrides./parameter_events.publisher.reliability
scientific_notation_num
state_interfaces
three_numbers
three_numbers_of_five
use_feedforward_commanded_input
use_sim_time
```

All parameter are automatically declared and callbacks are setup by default. You can set a parameter by typing:

`ros2 param set /admittance_controller control.frame.id new_frame`

You should see:

`[INFO] [1656019001.515820371] [admittance_controller]: New control frame parameter is: 'new_frame'`

Congratulations, you updated the parameter!

If you try to set a parameter that is read only, you will get an error. Running the following

`ros2 param set /admittance_controller command_interfaces ["velocity"]`

will result in the error

`Setting parameter failed: Trying to set a read-only parameter: command_interfaces.`

Running the following

`ros2 param describe /admittance_controller admittance.damping_ratio`

will show a parameter's description

```
Parameter name: admittance.damping_ratio
Type: double array
Description: specifies damping ratio values for x, y, z, rx, ry, and rz used in the admittance calculation. The values are calculated as damping can be used instead: zeta = D / (2 * sqrt( M * S ))
Constraints:
Min value: 0.1
Max value: 10.0
```

If you try to set a value out of the specified bounds,

`ros2 param set /admittance_controller admittance.damping_ratio [-10.0,-10.0,-10.0,-10.0,-10.0,-10.0]`

you will get the error

`Setting parameter failed: Value array('d', [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0]) in parameter 'admittance.damping_ratio' must be within bounds [0.1, 10.0]`

If you try to set a vector parameter with the wrong length,

`ros2 param set /admittance_controller admittance.damping_ratio [1.0,1.0,1.0]`

you will get the error

`Setting parameter failed: Length of parameter 'admittance.damping_ratio' is '3' but must be equal to 6`

If you try to load a yaml file with missing required parameters

`python3 src/generate_parameter_library/example_cmake_python/cmake_generate_parameter_module_example/minimal_publisher.py --ros-args --params-file src/generate_parameter_library/example_python/config/missing_required.yaml`

you will get the error

```
Traceback (most recent call last):
[...]
rclpy.exceptions.ParameterUninitializedException: The parameter 'fixed_string_no_default' is not initialized
[ros2run]: Process exited with failure 1
```
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Copyright 2023 PickNik Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the PickNik Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.


def no_args_validator(param):
return ''


def validate_double_array_custom_func(param, arg1, arg2):
return ''
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# Copyright 2023 PickNik Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the PickNik Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import rclpy
import rclpy.node

from cmake_generate_parameter_module_example.admittance_parameters import (
admittance_controller,
)


class MinimalParam(rclpy.node.Node):
def __init__(self):
super().__init__('admittance_controller')
self.timer = self.create_timer(1, self.timer_callback)

self.param_listener = admittance_controller.ParamListener(self)
self.params = self.param_listener.get_params()
self.get_logger().info(
"Initial control frame parameter is: '%s'" % self.params.control.frame.id
)
self.get_logger().info("fixed string is: '%s'" % self.params.fixed_string)

self.get_logger().info(
"Original joints parameter is: '%s'" % str(self.params.joints)
)
for d in self.params.fixed_array:
self.get_logger().info("value: '%s'" % str(d))

def timer_callback(self):
if self.param_listener.is_old(self.params):
self.param_listener.refresh_dynamic_parameters()
self.params = self.param_listener.get_params()
self.get_logger().info(
"New control frame parameter is: '%s'" % self.params.control.frame.id
)
self.get_logger().info("fixed string is: '%s'" % self.params.fixed_string)
for d in self.params.fixed_array:
self.get_logger().info("value: '%s'" % str(d))


def main(args=None):
rclpy.init(args=args)
node = MinimalParam()
rclpy.spin(node)


if __name__ == '__main__':
main()
Loading

0 comments on commit 55118ab

Please sign in to comment.