Claude CodeでROS 2パッケージを作る — colcon buildとlaunch_testの反復
Claude CodeにROS 2パッケージを書かせるとき、colcon buildとlaunch_testを完了条件にするCLAUDE.mdとpermissionsの設定例、sourceの扱いをまとめます。
ROS 2のパッケージをClaude Codeに書かせるなら、完了の合図は「colcon build が通り、colcon test が全件パスした」に固定するのが近道です。ビルドの成否もテストの成否もコマンドの出力と終了コードに出るので、Claude Codeが結果を読んで直す反復に向いています。
つまずきやすいのは別の場所にあります。ROS 2は source install/setup.bash で環境を作ってからでないと ros2 run も launch_test も動きません。しかもClaude Codeの許可ルールは、&& でつないだコマンドを1つずつ照合します。ワークスペースの構成、source、権限、テストの4点を CLAUDE.md と設定に先に書いておくと、反復が止まりにくくなります。
手順はLinuxとROS 2 Jazzyを前提にします。例はROS 2チュートリアルの構成に沿った書き方で、パッケージ名などは仮のものです。
ワークスペースの構成をClaude Codeに伝える
colconはソースツリーの外にビルド成果物を出す「out-of-source」ビルドです。ワークスペース直下の src と並んで、build、install、log の3ディレクトリが作られます。build は中間ファイル、install はパッケージごとの配置先、log は各実行のログです。catkinにあった devel ディレクトリはありません。
この構造は CLAUDE.md に書く価値があります。Claude Codeが編集してよいのは src 配下だけで、残り3つは生成物だからです。
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
git clone https://github.com/ros2/examples src/examples -b jazzyワークスペースは、既存のROS 2インストール(underlay)の上に重なるoverlayとして作ります。チュートリアルは、少数のパッケージを反復開発するならoverlayを使い、全パッケージを1つのワークスペースに入れないよう勧めています。underlayをsourceしていないと依存関係が解決できないので、ビルドの前提として CLAUDE.md に書きます。
次の CLAUDE.md は、書き方の一例です。
# ROS 2 ワークスペース
- ディストリビューション: Jazzy。underlay は /opt/ros/jazzy
- 編集してよいのは src/ 配下のみ。build/ install/ log/ は生成物なので触らない
- ビルド: colcon build --symlink-install --packages-select <pkg>
- テスト: colcon test --packages-select <pkg> の後に
colcon test-result --verbose で失敗したテスト名を読む
- ros2 run / launch_test の前に source install/setup.bash を同じコマンド行で実行する
- --cmake-args -DBUILD_TESTING=0 は付けない(テストがビルドされなくなる)
- 完了条件: 対象パッケージの colcon test-result が失敗 0 件Claude Codeのメモリのページは、指示を「検証できるほど具体的に」書くこと、1ファイルは200行以内を目安にすることを挙げています。「テストを通す」ではなく、コマンドと成功条件まで書くのはそのためです。
新しいパッケージの作り方を決めておく
colconは package.xml の仕様(REP 149)に従います。推奨のビルドタイプは ament_cmake と ament_python の2つで、純粋な cmake パッケージも扱えます。雛形は ros2 pkg create で作れます。
Claude Codeにパッケージを新規作成させるなら、ビルドタイプを先に決めて CLAUDE.md に書きます。C++ノードなら ament_cmake、Pythonだけなら ament_python と書いておくと、--symlink-install の効き方も予測できます。colcon build --mixin debug のように、よく使うビルドオプションをmixinに登録しておく方法もあります。登録は colcon mixin add と colcon mixin update で行います。
ビルドを速く回す — --symlink-installと --packages-select
反復の回転数を決めるのはビルド時間です。colcon build の --symlink-install を付けると、Pythonファイルなどコンパイルが要らないリソースは、src 側を直すだけで install 側に反映されます。ament_cmake のパッケージはインストールが必須で devel の概念がないため、このオプションが用意されている、とチュートリアルは説明しています。
どこまで再ビルドが要るか
ament_python のノード
Pythonファイルを直すだけで install 側に反映されます。再ビルドなしで再実行できます。
C++ のノード
コンパイルが要るので、直したら colcon build を回し直します。
Claude Codeに対象を絞らせるなら --packages-select を使います。CPUやメモリが厳しい環境(Raspberry Piなど)では画面やマウスが固まることがあり、チュートリアルは --executor sequential を挙げています。ビルドしたくないパッケージは、そのディレクトリに空の COLCON_IGNORE ファイルを置けば索引から外れます。
colcon testとcolcon test-result — 失敗を読ませる
テストは colcon test で走り、結果は別の動詞 colcon test-result で見ます。
colcon test --packages-select my_pkg
colcon test-result --all
colcon test-result --all --verbose--verbose を付けると、失敗したテストケースが個別に出ます。Claude Codeには「colcon test の後に colcon test-result --verbose を読んで、失敗したテスト名から直す」と指示すると、件数だけを見て終わる事態を避けられます。
テストの出力が見えないときは、Pythonテストの解説にある --event-handlers console_cohesion+ が使えます。pytestの出力がコンソールにそのまま出ます。1つのテストだけを回したいときは、次のように絞ります。
# CMake パッケージ(ctest)の1テストだけ
colcon test --packages-select my_pkg --ctest-args -R test_name
# Python パッケージ(pytest)の1テストだけ
colcon test --packages-select my_pkg --pytest-args -k test_nameCLIテストの解説によると、colcon test の前にworkspaceをsourceする必要はありません。colconが、テストに正しい環境と依存関係を用意するためです。sourceが要るのは、次のlaunch_testを単体で動かすときや ros2 run のときです。
launch_testを完了条件に入れる
ノードを実際に起動して確かめる統合テストは launch_testing のフレームワークで書きます。launch_testingのREADMEによると、起動したプロセスの終了コードと標準出力がテストから読め、プロセスが予期せず落ちたらテストを失敗にできます。
テストファイルには generate_test_description 関数を置き、launch_testing.actions.ReadyToTest() を含む LaunchDescription を返します。unittest.TestCase を継承した通常のクラスは起動中のプロセスと並行して走り、post_shutdown_test で修飾したクラスは終了後に走ります。次は、その構造に沿った例です(コマンド名は仮)。
import unittest
import launch
import launch_testing
import launch_testing.actions
import launch_testing.asserts
def generate_test_description():
return launch.LaunchDescription([
launch.actions.ExecuteProcess(cmd=['ros2', 'run', 'my_pkg', 'my_node']),
launch_testing.actions.ReadyToTest(),
])
class TestRunning(unittest.TestCase):
def test_prints_ready(self, proc_output):
proc_output.assertWaitFor('ready', timeout=10)
@launch_testing.post_shutdown_test()
class TestShutdown(unittest.TestCase):
def test_exit_code(self, proc_info):
launch_testing.asserts.assertExitCodes(proc_info)assertWaitFor の timeout は秒単位の待機で、既定は10です。待ち時間を超えると AssertionError になります。
colcon test の中で動かすには、CMakeLists.txt に登録します。統合テストの解説は、BUILD_TESTING の中で launch_testing_ament_cmake を探し、add_launch_test を呼ぶ構成を示しています。
if(BUILD_TESTING)
find_package(launch_testing_ament_cmake REQUIRED)
add_launch_test(test/test_integration.py)
endif()package.xml には <test_depend>launch_testing</test_depend> と <test_depend>launch_testing_ament_cmake</test_depend> も要ります。Claude Codeがテストを足したのに実行されない場合は、この登録と依存の漏れをまず疑います。登録前のテストファイルは、launch_test test/test_integration.py で単体実行して中身を確かめられます。
source install/setup.bashとpermissions
ここがROS 2特有の摩擦点です。Claude Codeの権限ルールは、&&、;、| などで区切られた各サブコマンドを個別に照合します。権限のページの例では、Bash(safe-cmd *) を許可しても safe-cmd && other-cmd は通りません。つまり source install/setup.bash && ros2 run my_pkg my_node は、source側と ros2 run 側の両方に許可ルールが要ります。
反復で使うコマンドは、次のように許可しておけます。
{
"permissions": {
"allow": [
"Bash(colcon build *)",
"Bash(colcon test *)",
"Bash(colcon test-result *)",
"Bash(source install/setup.bash)",
"Bash(launch_test *)"
],
"deny": [
"Edit(/install/**)",
"Edit(/build/**)",
"Edit(/log/**)"
]
}
}ルールの書き方には注意点が2つあります。末尾の * の前の空白はルールの一部で、Bash(colcon test *) は colcon test-result に一致しません。そのため test-result は別の行で許可しています。もう1つは、ルールがClaudeの書いたコマンド文字列に対する照合なので、/usr/bin/colcon のようにパスを付けた呼び出しは一致しない点です。
ros2 run や ros2 launch は、ノードを起動するコマンドです。長く動き続けるものを許可する前に、何が走るかを確かめてください。複数のコマンドを1つのルールに頼らず通したい場合の整理は、Bashの許可ルールと複合コマンドで扱っています。ルールの確認と管理には/permissionsコマンドが使えます。
underlayのsourceを毎回書かない方法
Claude Codeには、シェルの状態を次のコマンドへ持ち越す手段として CLAUDE_ENV_FILE があります。環境変数のページによると、これはBashコマンドの実行前に毎回実行されるシェルスクリプトのパスで、virtualenvやcondaの有効化を引き継ぐために使います。SessionStartフックはこのパスを受け取れるので、underlayのsourceをそこに書けます。
#!/bin/bash
# SessionStart フックのスクリプト例
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'source /opt/ros/jazzy/setup.bash' >> "$CLAUDE_ENV_FILE"
fi
exit 0overlayの install/setup.bash はビルドのたびに内容が変わるため、この方法に入れず、ros2を使うコマンドの直前でsourceする形にとどめています。
反復の流れをフックで締める
ここまでの設定で、1回の反復は「編集 → ビルド → テスト → test-result を読む → 直す」になります。Claude Codeが「できました」と返す前にテストを回させたい場合は、Stopフックを使います。PostToolUseとStopのどちらに置くかは、テストを自動実行するhooksで比較しています。
ZigやC++ など別言語で同じ設計をした例は、Claude CodeでZigを書くにあります。完了条件のコマンドを CLAUDE.md、permissions、フックの3層で固定する考え方は、ROS 2でもそのまま使えます。
ROS 2で起きやすいつまずき
- sourceを忘れる:
ros2 runが「パッケージが見つからない」系の失敗をするとき、新しいシェルでoverlayをsourceしていないことが多い。CLAUDE.mdに「同じコマンド行でsource」と書いてあれば、Claude Codeは自分で気づけます - underlayが未source: ビルド依存が解決できず、
colcon buildが最初のパッケージで止まります BUILD_TESTING=0: ビルド時間は縮みますが、テストがビルドされず、colcon testが実質的に何も確かめません。完了条件が空になるので、CLAUDE.mdで禁止しておきます- launch_testのタイムアウト: ノードの起動が遅いと
assertWaitForがAssertionErrorで落ちます。timeoutを延ばす前に、ノードが出力する文字列が変わっていないか確かめます
Windowsでは事情が異なります。colconのチュートリアルは、Visual Studioの環境が必要なこと、--merge-install を使うこと、シンボリックリンクの作成に管理者権限か開発者モードが要ることを挙げています。ここまでの設定はLinuxの話です。