最近話題の PicoClawを、数年前にAliexpressで買って積んでいた MaixCAMにセットアップしてみました。
その際のTipsです。
| 項目 | 内容 |
|---|---|
| デバイス | Sipeed MaixCAM |
| OS | maixcam 2026-01-24-maixpy-v4.12.5 (BusyBox v1.36.1) |
| init システム | init.d (SysVinit スタイル) |
| メモリ | 128MB RAM |
1. PicoClaw のセットアップ
-
https://github.com/sipeed/picoclaw/releases から最新RISC-V向けビルド済みバイナリファイル(例:
picoclaw_Linux_riscv64.tar.gz)をダウンロード -
上記
.tar.gzファイルを解凍してpicoclawファイルを入手し、 MaixCAM の/ROOT/ディレクトリに転送
(MaixVision を使うと簡単) -
SSH接続した MaixCAM のターミナルで以下を実行
# バイナリファイルに実行権限を与える
chmod +x picoclaw
# 初期化
./picoclaw onboard
あとは https://github.com/sipeed/picoclaw/blob/main/README.ja.md#-クイックスタートネイティブ を参考にする。
自分の場合は
- LLM プロバイダー: OpenRouter
- Web 検索: Brave Search
- チャットアプリ: Discode
と連携できるようにした。
2. PicoClaw Discord Gateway 自動起動設定
いちいちコマンドを打ち込んでPicoClawを立ち上げるのが面倒になったら、自動起動用の init.d スクリプトを作成して自動起動化する。
watchdogにより自動でPicoClawを再起動させるシェルスクリプトも併用する。
2.1. PicoClawを再起動させるシェルスクリプトの作成
cat > /root/picoclaw-watchdog.sh << 'EOF'
#!/bin/sh
PICOCLAW=/root/picoclaw
LOG=/var/log/picoclaw-watchdog.log
HEARTBEAT_LOG=/root/.picoclaw/workspace/heartbeat.log
CONFIG=/root/.picoclaw/config.json
HEARTBEAT_TIMEOUT=3600
CHECK_INTERVAL=60
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG"
}
# Extract Discord bot token from config.json (look inside "discord" section)
get_discord_token() {
grep -A10 '"discord"' "$CONFIG" | grep '"token"' | head -1 | sed 's/.*"token": *"\([^"]*\)".*/\1/'
}
# Extract last known Discord channel ID from heartbeat.log
get_discord_channel() {
grep -o 'discord:[0-9]*' "$HEARTBEAT_LOG" 2>/dev/null | tail -1 | cut -d: -f2
}
# Send Discord notification via Bot API
notify_discord() {
MSG="$1"
TOKEN=$(get_discord_token)
CHANNEL=$(get_discord_channel)
if [ -z "$TOKEN" ] || [ -z "$CHANNEL" ]; then
log "WARN: Discord token or channel not found, skip notification."
return 1
fi
PAYLOAD="{\"content\":\"$MSG\"}"
if which curl > /dev/null 2>&1; then
curl -s -o /dev/null \
-X POST \
-H "Authorization: Bot $TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"https://discord.com/api/v10/channels/$CHANNEL/messages" \
&& log "Discord notification sent (curl)." \
|| log "WARN: Discord notification failed (curl)."
elif which wget > /dev/null 2>&1; then
wget -q -O /dev/null \
--header="Authorization: Bot $TOKEN" \
--header="Content-Type: application/json" \
--post-data="$PAYLOAD" \
"https://discord.com/api/v10/channels/$CHANNEL/messages" \
&& log "Discord notification sent (wget)." \
|| log "WARN: Discord notification failed (wget)."
else
log "WARN: Neither curl nor wget available, skip notification."
fi
}
stop_picoclaw() {
killall picoclaw 2>/dev/null
# Clean up orphaned udevadm processes spawned by picoclaw device monitor
killall udevadm 2>/dev/null
sleep 2
}
start_picoclaw() {
log "Starting PicoClaw gateway..."
HOME=/root nohup $PICOCLAW gateway >> /var/log/picoclaw.log 2>&1 &
PICOCLAW_PID=$!
log "PicoClaw gateway started. PID=$PICOCLAW_PID"
echo "$PICOCLAW_PID" > /var/run/picoclaw.pid
PICOCLAW_STARTED=$(date +%s)
}
log "Watchdog started."
start_picoclaw
# Wait for PicoClaw to fully initialize, then send startup notification
sleep 15
notify_discord "PicoClaw started on MaixCAM at $(date '+%Y-%m-%d %H:%M:%S')"
while true; do
sleep "$CHECK_INTERVAL"
PID=$(cat /var/run/picoclaw.pid 2>/dev/null)
# Check 1: Is the process alive?
if [ -z "$PID" ] || ! kill -0 "$PID" 2>/dev/null; then
log "WARN: PicoClaw process not found. PID=$PID Restarting..."
stop_picoclaw
start_picoclaw
sleep 15
notify_discord "PicoClaw restarted on MaixCAM (crash recovery) at $(date '+%Y-%m-%d %H:%M:%S')"
continue
fi
# Check 2: Is heartbeat log stale? (process hung)
# Only check after running at least HEARTBEAT_TIMEOUT to avoid false positives on startup
NOW=$(date +%s)
UPTIME=$(expr $NOW - $PICOCLAW_STARTED)
if [ "$UPTIME" -gt "$HEARTBEAT_TIMEOUT" ] && [ -f "$HEARTBEAT_LOG" ]; then
LAST_BEAT=$(stat -c %Y "$HEARTBEAT_LOG" 2>/dev/null || echo 0)
ELAPSED=$(expr $NOW - $LAST_BEAT)
if [ "$ELAPSED" -gt "$HEARTBEAT_TIMEOUT" ]; then
log "WARN: Heartbeat stale for ${ELAPSED}s. Killing and restarting..."
stop_picoclaw
start_picoclaw
sleep 15
notify_discord "PicoClaw restarted on MaixCAM (heartbeat timeout) at $(date '+%Y-%m-%d %H:%M:%S')"
fi
fi
done
EOF
2.2. init.d スクリプト作成
cat > /etc/init.d/S99picoclaw << 'EOF'
#!/bin/sh
PICOCLAW=/root/picoclaw
WATCHDOG=/root/picoclaw-watchdog.sh
LOG=/var/log/picoclaw.log
WATCHDOG_LOG=/var/log/picoclaw-watchdog.log
start() {
echo "Starting PicoClaw watchdog..."
HOME=/root nohup $WATCHDOG >> $WATCHDOG_LOG 2>&1 &
echo "PicoClaw watchdog started."
}
stop() {
echo "Stopping PicoClaw and watchdog..."
killall picoclaw-watchdog.sh 2>/dev/null
killall picoclaw 2>/dev/null
rm -f /var/run/picoclaw.pid
echo "PicoClaw stopped."
}
case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 2; start ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
EOF
HOME=/root の明示が必須。これがないと ~/.picoclaw/config.json が見つからず起動失敗する。
2.3. 実行権限を与える
ssh root@<MaixCAMのIP> "chmod +x /root/picoclaw-watchdog.sh && chmod +x /etc/init.d/S99picoclaw && sed -i 's/\r//' /root/picoclaw-watchdog.sh"
2.4. 動作確認
# 手動起動テスト
/etc/init.d/S99picoclaw start
# ログ確認
tail -f /var/log/picoclaw.log
# プロセス確認
ps | grep picoclaw
正常起動時のログ例:
[INFO] channels: Starting channel {channel=discord}
[INFO] discord: Starting Discord bot
[INFO] discord: Discord bot connected {username=MaixCAM-PicoClaw}
[INFO] channels: All channels started
✓ Health endpoints available at http://0.0.0.0:18790/health and /ready
2.3. 再起動後の確認
reboot
# 再起動後
ps | grep picoclaw
tail /var/log/picoclaw.log
3. tailscale のセットアップ・自動起動
ローカルネットワーク外からもアクセスできるように、tailscale をセットアップして自動起動できるようにする。
3.1. セットアップ
-
https://pkgs.tailscale.com/stable/#static から、から最新RISC-V向けビルド済みバイナリファイル(例:
tailscale_1.94.2_riscv64.tgz)をダウンロード -
上記
.tar.gzファイルを解凍してtailscaleファイルとtailscaledファイルを入手し、 MaixCAM の/usr/bin/ディレクトリに転送
(MaixVisionでは無理なので、scpコマンドを使う)bash$ scp tailscale_1.94.2_riscv64/tailscale root@192.168.x.x:/usr/bin/ $ scp tailscale_1.94.2_riscv64/tailscaled root@192.168.x.x:/usr/bin/ -
SSH接続した MaixCAM のターミナルで以下を実行
bashtailscaled --state=/var/lib/tailscale/tailscaled.state tailscale up認証URLが表示されるので、ブラウザで開いて tailscale アカウントで認証。
3.2 自動起動化
以下で自動起動用の init.d スクリプトを作成
cat > /etc/init.d/S98tailscale << 'EOF'
#!/bin/sh
TAILSCALED=/usr/bin/tailscaled
TAILSCALE=/usr/bin/tailscale
STATE_DIR=/var/lib/tailscale
start() {
echo "Starting tailscaled..."
mkdir -p $STATE_DIR
$TAILSCALED --state=$STATE_DIR/tailscaled.state &
sleep 2
$TAILSCALE up
echo "tailscaled started."
}
stop() {
echo "Stopping tailscaled..."
killall tailscaled 2>/dev/null
echo "tailscaled stopped."
}
case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
EOF
# 実行権限を与える
chmod +x /etc/init.d/S98tailscale
3.3. (備考)起動シーケンスまとめ
init.d のスクリプトはファイル名の番号順に実行される。
| スクリプト | 内容 |
|---|---|
| S98tailscale | Tailscale VPN デーモン起動 |
| S99picoclaw | PicoClaw Discord Gateway 起動 |
4. メモリ対策
4.1. 事前確認
(PicoClawを立ち上げていない状態で)実機でメモリ使用量を確認した結果、既に空き容量が4MBしかなかったので、Swap ファイルを作成してメモリを空ける。
# メモリ使用量確認
free -h
total used free shared buff/cache available
Mem: 127.8M 63.2M 5.7M 316.0K 58.9M 57.1M
Swap: 0 0 0
# プロセスごとのメモリ使用量確認
ps -o pid,comm,vsz,rss | sort -k4 -rn | head -20
主なメモリ使用プロセス(実測値)
| プロセス | RSS |
|---|---|
| tailscaled | 37MB |
| launcher | 13MB |
| sshd | 5MB |
| wpa_supplicant | 4MB |
| bluetoothd | 2.6MB |
| vsftpd | 1.9MB |
4.2. Swap ファイル作成
tailscaled が 37MB 使用するため、Swap ファイルを作って逃がす
# 128MB の Swap ファイル作成
dd if=/dev/zero of=/swapfile bs=1M count=128
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 確認
free -h
# 再起動後も有効化(fstab に追記)
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
4.3. 結果
free -h
total used free shared buff/cache available
Mem: 127.8M 64.0M 4.5M 320.0K 59.2M 57.9M
Swap: 128.0M 0 128.0M
5. スキルの開発
スキルを実装することで、ある特定のワードをトリガーにPythonやシェルスクリプトを発動させることができる。
フォーマットは https://github.com/sipeed/picoclaw/blob/main/workspace/skills/skill-creator/SKILL.md を参考
6. その他もろもろ
記事にするのめんどくさくなったのでGitHubのリポジトリを見て。
Discodeと連携して、MaixCamで撮った画像をDiscodeのチャンネルに送ったりする方法を実装してる。
ちなみに自分がやった作業はClaude.mdに方針を示してClaudeCodeに指示を与えただけ。
ssh接続してMaixCAM内のファイル操作は基本的にClaudeCodeにやってもらってる。
参考