diff --git a/README.ja.md b/README.ja.md
index 0468ae8..92e5412 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -207,6 +207,8 @@ Hardwareがactiveの間は、各モーターの動作状態を監視します。
```
+`kp`と`kd`はmodelの既定値ではなく、実際に使用されるcontrol gainであるため必須引数です。robotの機構と負荷に合わせて選定・検証してください。上記の値は使用例であり、推奨値ではありません。
+
`4000`は本packageの安全上の既定値であり、モーターのfactory defaultではありません。公式manualでは`0`がfactory defaultでwatchdog無効、`20000` ticksが1秒です。本packageはhostからの指令が失われた場合にモーターをResetへ移行させるため、`0`を受け付けません。
## Controllerとcommand mode
diff --git a/README.md b/README.md
index 8bbd402..f809b5f 100644
--- a/README.md
+++ b/README.md
@@ -245,6 +245,10 @@ Example:
```
+`kp` and `kd` are required macro arguments because they are active control
+gains, not model defaults. Select and validate them for the robot mechanism and
+load. The values above are examples only.
+
The common `4000` watchdog value is a package safety default, not a factory default.
The official manuals document `0` as the factory value, which disables the
watchdog, and `20000` ticks as 1 second. This package rejects zero so that loss
diff --git a/robstride_examples/CHANGELOG.rst b/robstride_examples/CHANGELOG.rst
index f234e4e..2041b06 100644
--- a/robstride_examples/CHANGELOG.rst
+++ b/robstride_examples/CHANGELOG.rst
@@ -5,4 +5,5 @@ Changelog for package robstride_examples
Forthcoming
-----------
* Add launch, controller configuration, and RS/EduLite actuator profiles.
+* Require explicit ``kp`` and ``kd`` arguments for every motor-profile macro.
* Contributors: Yamato.K
diff --git a/robstride_examples/CMakeLists.txt b/robstride_examples/CMakeLists.txt
index eed8232..47ad956 100644
--- a/robstride_examples/CMakeLists.txt
+++ b/robstride_examples/CMakeLists.txt
@@ -6,4 +6,9 @@ find_package(ament_cmake REQUIRED)
install(DIRECTORY config description launch DESTINATION share/${PROJECT_NAME})
install(FILES LICENSE DESTINATION share/${PROJECT_NAME})
+if(BUILD_TESTING)
+ find_package(ament_cmake_pytest REQUIRED)
+ ament_add_pytest_test(test_motor_profile_gains test/test_motor_profile_gains.py)
+endif()
+
ament_package()
diff --git a/robstride_examples/description/robstride_motor_profiles.xacro b/robstride_examples/description/robstride_motor_profiles.xacro
index cf7b406..0724167 100644
--- a/robstride_examples/description/robstride_motor_profiles.xacro
+++ b/robstride_examples/description/robstride_motor_profiles.xacro
@@ -18,56 +18,56 @@
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-33.033.0-14.014.0
-14.014.0500.05.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-44.044.0-17.017.0
-17.017.0500.05.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-44.044.0-17.017.0
-17.017.0500.05.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-20.020.0-60.060.0
-60.060.05000.0100.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-15.015.0-120.0120.0
-120.0120.05000.0100.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-50.050.0-5.55.5
-5.55.5500.05.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-50.050.0-36.036.0
-36.036.05000.0100.0
${kp}${kd}${direction}${gear_ratio}${position_offset}
-
+
${can_id}${can_timeout_ticks}-12.56637061412.566370614
-50.050.0-6.06.0
-6.06.0500.05.0
diff --git a/robstride_examples/package.xml b/robstride_examples/package.xml
index 5286c8d..c599d38 100644
--- a/robstride_examples/package.xml
+++ b/robstride_examples/package.xml
@@ -23,6 +23,7 @@
ros2_socketcan
velocity_controllers
xacro
+ ament_cmake_pytest
ament_cmake
diff --git a/robstride_examples/test/test_motor_profile_gains.py b/robstride_examples/test/test_motor_profile_gains.py
new file mode 100644
index 0000000..0ad1767
--- /dev/null
+++ b/robstride_examples/test/test_motor_profile_gains.py
@@ -0,0 +1,49 @@
+from pathlib import Path
+from xml.dom import minidom
+
+import pytest
+import xacro
+
+
+PROFILE_XACRO = (
+ Path(__file__).resolve().parents[1] / "description" / "robstride_motor_profiles.xacro"
+)
+PROFILE_MACROS = [
+ "robstride_rs00_params",
+ "robstride_rs01_params",
+ "robstride_rs02_params",
+ "robstride_rs03_params",
+ "robstride_rs04_params",
+ "robstride_rs05_params",
+ "robstride_rs06_params",
+ "robstride_edulite05_params",
+]
+
+
+def process_profile(macro: str, arguments: str) -> None:
+ document = minidom.parseString(
+ f"""
+
+
+
+
+ """
+ )
+ xacro.process_doc(document)
+
+
+@pytest.mark.parametrize("macro", PROFILE_MACROS)
+def test_profile_requires_kp(macro: str) -> None:
+ with pytest.raises(xacro.XacroException):
+ process_profile(macro, 'kd="1.0"')
+
+
+@pytest.mark.parametrize("macro", PROFILE_MACROS)
+def test_profile_requires_kd(macro: str) -> None:
+ with pytest.raises(xacro.XacroException):
+ process_profile(macro, 'kp="30.0"')
+
+
+@pytest.mark.parametrize("macro", PROFILE_MACROS)
+def test_profile_accepts_explicit_gains(macro: str) -> None:
+ process_profile(macro, 'kp="30.0" kd="1.0"')
diff --git a/robstride_examples/test/test_motor_profiles.urdf.xacro b/robstride_examples/test/test_motor_profiles.urdf.xacro
index 4c808e7..1137761 100644
--- a/robstride_examples/test/test_motor_profiles.urdf.xacro
+++ b/robstride_examples/test/test_motor_profiles.urdf.xacro
@@ -5,13 +5,13 @@
robstride_ros2/RobStrideSystem
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+