ssl-RACOON-Controller などから送信された指令値をもとに、高速に情報を受信・制御を行うロボット側ソフトウェアです。
Pi 4B(UART) と Rock5A(SPI) の2ボードに対応しています。ビルド時に -tags でボードを明示指定してください。
Note: 旧
rock5aブランチの実装は本リポジトリに統合済みです。rock5aブランチは廃止しました。
cmd/
racoon-pi3/ # メインエントリポイント
dip_test/ # Rock5A DIP 診断ツール
spi_test/ # Rock5A SPI 診断ツール
internal/
app/ # 起動・goroutine オーケストレーション
localization/ # 車輪・Vision による自己位置推定(IMU 融合は未実装)
control/ # 経路補間・追従・速度フィードバック・制限
supervisor/ # モード・指令期限・推定鮮度の判定
locadapter/ # センサー受信値から推定入力への変換
stmframe/ # STM フレーム定義・デコード
timesync/ # 観測時刻の同期
loclog/ # 記録・再生
locsim/ # 推定器のシミュレーション検証
state/ # 既存通信経路の共有状態・データ構造
link/ # UART/SPI 共通リンクロジック
receive/ # AI / カメラ UDP 受信
mw/ # RACOON-MW への状態送信・推定結果の受け渡し型
api/ # HTTP API
upgrade/ # 自動アップデート
pi4/ # Pi 4B 専用(UART, go-rpio)
rock5a/ # Rock5A 専用(SPI, rock5a-gpio-go, sysfs PWM)
proto/ # Protobuf 定義・生成コード
camera/ # カメラ処理(Python)
capture/ # ボード別カメラ入力(pi4=Picamera2 / rock5a=V4L2)
detect/ # color.py(HSV 検出), calib.py(YOLO キャリブ)
transport/ # UDP 送信・エンコード
yolo/ # git submodule: Rione/ssl-YOLO-Detection
責務・接続経路・実装済み範囲は ローカル制御アーキテクチャ を参照してください。
新しい制御周期の接続点はハードウェア非依存です。実機の Run への接続は未実施です。
カメラの YOLO モデルは git submodule(Rione/ssl-YOLO-Detection)として camera/yolo/ に含まれます。submodule ごと取得してください。
git clone --recurse-submodules https://github.com/Rione/ssl-RACOON-Pi3.git
# 既にクローン済みの場合
git submodule update --init --recursive# Raspberry Pi 4B(UART / go-rpio)
go build -tags pi4 -o racoon-pi3 ./cmd/racoon-pi3
# Rock5A(SPI / rock5a-gpio-go)
go build -tags rock5a -o racoon-pi3 ./cmd/racoon-pi3
# Rock5A 診断ツール(開発 PC 上で Linux/arm64 向けにクロスビルドしてボードへ配置)
GOOS=linux GOARCH=arm64 go build -tags rock5a -o dip-test ./cmd/dip_test
GOOS=linux GOARCH=arm64 go build -tags rock5a -o spi_test ./cmd/spi_test
# 例: scp または ssh 経由で Rock5A へコピー
# scp spi_test root@<robot>:/root/spi_testRock5A 上で go build した場合は GOOS/GOARCH は不要です。Mac 等でビルドしたバイナリをそのままコピーしても アーキテクチャ不一致で動きません(または古いバイナリのまま)。必ず GOOS=linux GOARCH=arm64 でビルドしてください。
タグ未指定の go build . は不可です。
今ロボットで動いているのは Pi2 で、Pi2 は Pi2 のリリースしか取りに行きません。 機体上の配置は Rock5A の場合次のとおりです(2026-09-22 に robot 15 で確認)。
| もの | 場所 |
|---|---|
| 起動設定 | /etc/systemd/system/ssl-racoon.service(写しは scripts/ssl-racoon.service) |
| Pi2 のバイナリ | /root/racoon-pi2-rock5a |
| 作業ディレクトリ | /root(ボール色のしきい値は /root/threshold.json、カメラの Python は /root/camera/) |
そのため Pi3 は最初の 1 回を手で載せます。どのブランチのコードでも、ビルドして載せれば確かめられます (master やリリースを経由する必要はありません)。開発ビルドは版番号が開発版扱いになり、自己更新しません。
Pi2 の置き場所を上書きせず、別のディレクトリに置きます。戻すときはサービスを起動し直すだけで Pi2 に戻れます。
# 開発 PC で(確かめたいブランチのまま)
GOOS=linux GOARCH=arm64 go build -tags rock5a -o racoon-pi3 ./cmd/racoon-pi3
ssh root@<robot> 'mkdir -p /root/racoon-pi3'
scp racoon-pi3 root@<robot>:/root/racoon-pi3/
scp -r camera root@<robot>:/root/racoon-pi3/ # 搭載カメラを使う場合# ロボット上で
cp /root/threshold.json /root/racoon-pi3/ # ボール色の HSV しきい値を引き継ぐ
sudo systemctl stop ssl-racoon.service # Pi2 を止める
cd /root/racoon-pi3 && sudo ./racoon-pi3 -dryrun # まずは車輪に指令を送らずに起動- 必ず置き場所へ
cdしてから起動してください。threshold.jsonは作業ディレクトリから読み (無ければ既定値で新しく作る)、カメラの Python(camera/)はバイナリと同じディレクトリから起動します。 -dryrunは SPI へ速度・ドリブル・キックを送りません。受信・通信・ログを、ロボットを動かさずに確かめられます。 問題なければ-dryrunを外して走らせます。- Pi2 に戻す: Ctrl+C で止めて
sudo systemctl start ssl-racoon.service。Pi2 の置き場所には触れていないので元どおりです。 ロボットを再起動した場合も Pi2 が起動します。 - 自己位置推定の計測(
-loclogなど)はdocs/localization-p1-runbook.mdを参照してください。
機体を Pi3 で常用にするときの手順です。リリース版(タグを付けて作ったもの)を入れてください。 開発ビルドは自己更新しないので、入れたまま放置すると以後の更新を受け取れません。
# ロボット上で(例: v7.0.0)
cd /root
curl -fL -o /tmp/pi3.tar.gz \
https://github.com/Rione/ssl-RACOON-Pi3/releases/download/v7.0.0/racoon-pi3-rock5a_7.0.0_linux_arm64.tar.gz
tar -xzf /tmp/pi3.tar.gz -C /root # /root/racoon-pi3-rock5a と /root/camera/ ができる(.pt は上書きしない)
# 起動設定を Pi3 用に差し替える(開発 PC から scripts/ssl-racoon.service を持ってくる)
cp /etc/systemd/system/ssl-racoon.service /root/ssl-racoon.service.pi2.bak
cp ssl-racoon.service /etc/systemd/system/ssl-racoon.service
systemctl daemon-reload
systemctl restart ssl-racoon.service
journalctl -u ssl-racoon.service -f # 起動ログを確認(Ctrl+C で抜ける)threshold.jsonとcamera/は Pi2 と同じ/rootを使うので、しきい値はそのまま引き継がれます。- 追加設定
ssl-racoon.service.d/wait-6ghz.conf(Wi-Fi の 6 GHz 切替を待ってから起動する)は 別ファイルなので、上の差し替えでは消えません。写しはscripts/ssl-racoon.service.d/にあります。 待ち合わせ先のwifi-prefer-6ghz.serviceは機体上にだけあり、中身は取得していません。機体に残ったままなので載せ替えには不要です (要るのは新しい SD カードで機体を一から作るときです)。 - Pi2 に戻す:
cp /root/ssl-racoon.service.pi2.bak /etc/systemd/system/ssl-racoon.service && systemctl daemon-reload && systemctl restart ssl-racoon.service。 Pi2 のバイナリ(/root/racoon-pi2-rock5a)は消していないので、そのまま起動します。 - Pi 4B の機体の起動設定はまだ取得していません(バイナリ名は
racoon-pi2-pi4のはず)。ローカル制御は Rock5A の世代から 始めるので、旧世代を Pi3 に載せ替えると決まったときに取得します。
カメラ処理は Python の camera/ パッケージが担当します。通常運転では軽量な HSV + 輪郭検出のみを行い、検出結果を UDP(ポート 31133)で Go 本体へ送信します。Go 本体は起動時に python3 -m camera を実行し、ビルドタグに応じて環境変数 RACOON_BOARD(pi4 / rock5a)を渡します。
| ボード | バックエンド | デバイス |
|---|---|---|
| Pi 4B | Picamera2(MIPI CSI) | Picamera2 既定(CSI ポートはオーバーレイで指定) |
| Rock5A | OpenCV V4L2 | /dev/video11(既定)。threshold.json の cameraDevice で上書き可 |
Pi 4B には CSI コネクタが CAM0 と CAM1 の 2 つあります。接続ポートに応じて /boot/firmware/config.txt でオーバーレイを指定する必要があります。camera_auto_detect=1 だけでは検出に失敗することがあります(rpicam-hello --list-cameras が No cameras available! になる)。
| センサー | モジュール | オーバーレイ例 |
|---|---|---|
| IMX219 | Pi Camera v2 | dtoverlay=imx219,cam0 または dtoverlay=imx219,cam1 |
| OV5647 | Pi Camera v1.3 | dtoverlay=ov5647,cam0 または dtoverlay=ov5647,cam1 |
手動設定の例(CAM1 に IMX219 を接続した場合):
camera_auto_detect=0
dtoverlay=imx219,cam1-
起動時のオーバーレイ自動選択:
scripts/select-pi4-camera.shがimx219,cam0→imx219,cam1→ov5647,cam0→ov5647,cam1の順に試します。scripts/racoon-camera-autoselect.serviceは Pi(/boot/firmware/config.txt)と Rock5A(/boot/dietpiEnv.txt)のどちらでも動作します。sudo install -m 0755 scripts/select-pi4-camera.sh /usr/local/sbin/select-pi4-camera.sh sudo install -m 0755 scripts/select-rock5a-camera.sh /usr/local/sbin/select-rock5a-camera.sh sudo install -m 0644 scripts/racoon-camera-autoselect.service /etc/systemd/system/racoon-camera-autoselect.service sudo systemctl daemon-reload sudo systemctl enable racoon-camera-autoselect.service -
映像プレビュー:
http://<robot>:9191/color-tunerでライブプレビューと HSV しきい値調整ができます。 -
上下左右反転(180°回転):
threshold.jsonの"cameraFlip180": true/falseで制御します。IMX219 の既定は反転あり(RACOON 取り付け向きに合わせた設定)。 -
キャプチャ解像度(Pi 4B): IMX219 は全画角を維持する最小解像度 1640×1232(2×2 ビニング)を既定にしています。640×480 はセンサー中央の切り出しになるため使いません。
threshold.jsonの"frameWidth"/"frameHeight"で上書きできます。
Rock5A は Raspberry Pi Camera v1.3(OV5647)と v2(IMX219)の両方に対応しますが、それぞれ別のデバイスツリーオーバーレイを使い、同時に有効化すると CSI パイプラインが壊れます(rkcif ... get remote terminal sensor failed)。
| センサー | モジュール | オーバーレイ | I2C アドレス |
|---|---|---|---|
| IMX219 | Pi Camera v2 | rpi-camera-v2 |
0x10 |
| OV5647 | Pi Camera v1.3 | rpi-camera-v1_3 |
0x36 |
-
起動時のオーバーレイ自動選択:
scripts/select-rock5a-camera.shを systemd の oneshot(scripts/racoon-camera-autoselect.service)としてssl-racoon.serviceより前に実行します。センサーの subdev(imx219/ov5647)が出ていなければ/boot/dietpiEnv.txtのoverlays=を片方だけになるよう書き換えて 1 回だけ再起動します。状態ファイル/var/lib/racoon-camera-autoselect.stateで IMX219↔OV5647 を最大 1 巡しか試さないため、リブートループにはなりません。正しいオーバーレイが既に設定済み(通常運転)なら何もしません。Pi 4B 向けは上記「Pi 4B の CSI オーバーレイ」を参照してください。# 初回のみ(Rock 5A 端末上で。Pi 4B は select-pi4-camera.sh も併せて install) sudo install -m 0755 scripts/select-rock5a-camera.sh /usr/local/sbin/select-rock5a-camera.sh sudo install -m 0644 scripts/racoon-camera-autoselect.service /etc/systemd/system/racoon-camera-autoselect.service sudo systemctl daemon-reload sudo systemctl enable racoon-camera-autoselect.service
-
露出・ゲインのセンサー別設定:
camera/sensor.pyは/sys/class/video4linux/v4l-subdev*/nameから接続中センサーを判別し、センサーごとに正しいコントロール名・範囲で露出/ゲインを適用します。IMX219 は明るさを実効ゲイン(gain、最大 43663。analogue_gainではない)で稼ぎ、動きブレを抑えるため露出は低め(既定exposure=1000/gain=5000)にします。OV5647 は従来どおりauto_exposure/gain_automatic/analogue_gainを使用します。threshold.jsonのcameraExposure/cameraGain/cameraAutoExposure/cameraSensorSubdevで上書きできます。 -
上下左右反転(180°回転): センサーごとに既定値があります(IMX219: 反転あり / OV5647: 反転なし)。RACOON の取り付け向きに合わせた設定です。
threshold.jsonの"cameraFlip180": true/falseまたは環境変数CAMERA_FLIP180で接続中センサーに対して上書きできます。個別のcameraHFlip/cameraVFlipも引き続き利用可能です。反転はソフトウェア側で行います(センサー側 flip は Bayer デモザイクの都合で色が緑に寄るため使いません)。 -
IMX219 の色補正: Rockchip ISP + IMX219 ではドライバ AWB がなく緑被りが出やすいため、IMX219 接続時は既定で BGR ゲイン補正(
1.15, 0.78, 1.12)を適用します。threshold.jsonの"cameraColorGains": "1.15,0.78,1.12"(B,G,R 順)で上書きできます。OV5647 では既定では適用しません。
pip install -r camera/requirements.txtpicamera2 は Pi 4B のみ必要です。ultralytics(YOLO)はキャリブレーション時のみ遅延 import されます。
Raspberry Pi 上で常時 YOLO を動かすのは負荷が高いため、YOLO はキャリブレーション時のみ使用します。GET /calibballcolor を叩くと、カメラプロセスが 1 フレームを YOLO で推論し、検出したボールのバウンディングボックス中心と上下左右 4 点(計 5 点)から HSV を算出して threshold.json を更新します。以降は通常の HSV 検出が新しいしきい値で動作します(プロセス再起動不要)。
# ボールをカメラに写した状態で実行
curl http://<robot>:9191/calibballcolor成功時はしきい値・バウンディングボックス・サンプル点・プレビュー画像(base64 JPEG)を含む JSON を返します。ボール未検出時は HTTP 400 を返します。
GitHub Release からボード別バイナリを取得します。
仕組み:
- 起動時に 1 回だけ確認します(
internal/app/run.go→internal/upgrade)。取得先はこのリポジトリ(Rione/ssl-RACOON-Pi3)のリリースです。 - 新しい版があれば実行中のバイナリ自身を上書きし、リリースに同梱の
camera/もバイナリの隣へ展開して、 プロセスを終了します。再起動は systemd に任せています。 - 開発ビルド(タグの付いていないコミットや、未コミットの変更入りでビルドしたもの。版が
v0.0.0-…や…+dirtyになる)は確認そのものをしません。 - ロボットの起動時にこのプログラムを立ち上げるのは systemd の
ssl-racoon.serviceです(Rock5A 用の写しはscripts/ssl-racoon.service)。Restart=alwaysで立ち上げ直し、 ネットワーク(と Wi-Fi の 6 GHz 切替)を待ってから起動します。起動時にネットワークが無いと、その回の確認は失敗し、次の起動まで再試行しません。
Public リポジトリのため .env や GITHUB_TOKEN は必須ではありません。.env がある場合は自動で読み込みます(API レート制限を避けたい場合に GITHUB_TOKEN を設定できます)。
| ビルド | Release アセット名(例) | フィルタ |
|---|---|---|
| Pi 4B | racoon-pi3-pi4_7.0.0_linux_arm64.tar.gz |
^racoon-pi3-pi4_ |
| Rock5A | racoon-pi3-rock5a_7.0.0_linux_arm64.tar.gz |
^racoon-pi3-rock5a_ |
Release の tar には Go バイナリと camera/ の Python ソース(YOLO モデル .pt は含まない)が同梱されます。キャリブレーション用の重みは Release ごとに 1 つだけ別アセット(racoon-pi3-yolo_<version>_last.pt、約 23 MiB)として公開します。初回セットアップ時にロボットへ配置してください。
# バイナリと同じディレクトリで(git clone 時は submodule でも可)
./scripts/install-yolo-model.sh v7.0.0
# または手動:
# curl -fL -o camera/yolo/last.pt \
# https://github.com/Rione/ssl-RACOON-Pi3/releases/download/v7.0.0/racoon-pi3-yolo_7.0.0_last.pt自動アップデートはバイナリと Python のみ同期し、既にある camera/yolo/*.pt は上書きしません(毎回 ~23 MiB×2 の転送を避けるため)。
- タグを push するとリリースされます(
.github/workflows/main.yml→ goreleaser)。版番号はタグ名そのものです。 - Pi3 の版番号は
v7.0.0から始めます。 Pi2 の最終系列(v6.x)の続きとして数えるため、 Pi2 と Pi3 の機体が混在する移行期でも、RAVEN の Robot Status で「v7 以上なら Pi3」と一目で分かります。 - 自己更新は このリポジトリ(Rione/ssl-RACOON-Pi3)のリリース同士でしか比べません。 Pi2 で動いている機体は Pi2 のリリースしか見ないので、Pi2 から Pi3 への最初の載せ替えは手作業になります。 一度 Pi3 を載せれば、以後は Pi3 のリリースで自動更新されます。
- Pi2 のリリースタグ(v0.1.0〜v6.x)はこのリポジトリへ持ち込んでいません。Pi2 のタグを push しないでください (goreleaser は直近のタグから版を決めるため、Pi3 が Pi2 の版番号でリリースされてしまいます)。
ロボットIDには、ロボットに内蔵されたDIPスイッチよりIDの検出を行います。 カバーと同じ色にIDを設定するようにしてください。
Raspberry Pi 4B 向け。STM との通信は UART(/dev/serial0 @ 230400 baud)です。
| 名称 | ピン番号/ポート名 |
|---|---|
| Serial(UART) | /dev/serial0 |
| LED 1 | GPIO 18 |
| LED 2 | GPIO 27 |
| Button 1 | GPIO 22 |
| Button 2 | GPIO 24 |
| Buzzer | GPIO 13(PWM) |
| DIP 1 | GPIO 4 |
| DIP 2 | GPIO 5 |
| DIP 3 | GPIO 6 |
| DIP 4 | GPIO 25 |
UART を使用する際には設定が必要です。
sudo raspi-config
Radxa Rock5A 向け。STM との通信は SPI Master(/dev/spidev4.0 @ 1 MHz, Mode0)です。送受信フレーム長は 20 バイト(ヘッダ 0xFF + ペイロード 18 バイト + フッタ 0xAA)。受信ペイロードの先頭 11 バイトが有効データで、続く 7 バイトはパディング(0x00)です。ヘッダ・フッタ・パディングが不正なフレームは破棄されます。
| 名称 | Rock5A GPIO | 物理ピン |
|---|---|---|
| SPI | /dev/spidev4.0 | - |
| LED 1 | GPIO4_A1 (bank4,portA,pin1) | Pin 12 |
| LED 2 | GPIO4_B2 (bank4,portB,pin2) | Pin 13 |
| Button 1 | GPIO4_B4 | Pin 15 |
| Button 2 | GPIO1_B0 | Pin 18 |
| Buzzer (PWM) | Pin11 = PWM15 | Pin 11 |
| DIP 1 | GPIO1_B3 | Pin 7 |
| DIP 2 | GPIO1_B2 | Pin 29 |
| DIP 3 | GPIO1_B1 | Pin 31 |
| DIP 4 | GPIO1_B5 | Pin 22 |
ブザー PWM にはデバイスツリーオーバーレイ rk3588-pwm15-m1 の有効化が必要です。
sudo /root/spi_test -interval 8ms # 本番と同じ 125Hz(SignalReceived のみ、EmgStop=0)
sudo /root/spi_test -once # 1 回送信(TX 20 バイト)
sudo /root/spi_test -interval 8ms -velx 500 -charge # 走行テスト(DoCharge も付与)
sudo /root/spi_test -emgstop # 起動直後 idle 相当(EmgStop=1、走行不可)
sudo /root/spi_test -interval 8ms -mismatch-only # NG のみ表示
# Ctrl+C で OK/NG パケット統計を表示Informations の bit0 (EmgStop) は 1=非常停止中 です。MW から指令を受けている本番状態では 0 です。
初期ホスト名 DietPi の場合、初回起動時に racoon-XXXXX 形式のホスト名へ自動変更されます。
このリポジトリは ssl-RACOON-Pi2 の master(f183f35)から派生しました。
ロボット上のローカル制御(自己位置推定・追従制御)への移行をここで進めます。
Pi2 のコミット履歴はそのまま引き継いでいるので、それ以前の経緯は git log / git blame で辿れます。
Pi2 の feat/#1-self-localization(8860429)と feat/#2-set-velocity(ae433a8)も取り込み済みです。