WPP
arm64 ROS2 環境を podman で動かす

いろいろと紆余曲折ありましたが、最終的に podman で動かすことで落ち着きそうなので記事にします。

d最近の私のお供は Claude なのですが早速聞いてみますとつらつらと Dockerfile を提示してきます。多少アレンジしてますが骨子はそのままです。

FROM --platform=linux/arm64 ubuntu:22.04


ENV DEBIAN_FRONTEND=noninteractive

RUN rm /var/lib/dpkg/info/libc-bin.* && apt-get clean

# 日本のミラーに向ける
RUN sed -i 's@archive.ubuntu.com@ftp.jaist.ac.jp/pub/Linux@g' /etc/apt/sources.list

RUN apt-get update && \
    apt-get install -y wget \
    curl \
    git \
    vim \
    lsb-release \
    build-essential python3-pip python3-setuptools python3-dev \
    gnupg2 xterm x11-apps

# ROSのセットアップ(ここでは例としてROS 2 Humbleを使用)
RUN curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null
RUN apt-get update && apt-get install -y \
    ros-humble-ros-base \
    ros-humble-rqt* \
    ros-humble-rviz2 \
    python3-colcon-common-extensions

# 作業ディレクトリの作成
RUN mkdir -p /workspace
WORKDIR /workspace

# 環境変数の設定
RUN echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc

変更かけたのは、 sed で日本のミラーに向けたところと、rm /var/lib/dpkg/info/libc-bin.* でファイルを削除した箇所です。

特にこの libc-bin.* の削除をしないと、apt-get でのパッケージインストールにことごとく失敗するします。なんとなく削除するのは良くないような気もするのですが、 Stack Overflow でもいくつか Good がついているので大丈夫じゃないかと。

setup.bash もすでに読み込み済みなので起動すると即 ros2 コマンドが実行できる状態になっています。

ビルドと起動は下のようにします。

Claud は気が利いているのどうなのかこの状態で GUI アプリも動作出来るようにしてくれているようでコンテナを実行するにはこうするみたいです。

$  ls ./Dockerfile
./Dockerfile

$ podman build -t ros2-arm64 .

$ podman run --platform linux/arm64 \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -e DISPLAY=$DISPLAY \
  -v $HOME/.Xauthority:/root/.Xauthority \
  -v $HOME/ros2_ws:/workspace \
  --net=host \
  -it localhost/ros2-arm64

起動のたびにこのコマンド打つのはしんどいのでシェルスクリプトかなんかにしたほうがよさげ。

でコンテナの中で動作確認するには、下のようにしろと Claude が言うので試してみる。

; ホストに 目玉アプリが表示される。
in-contailner: # xeyes

; ホストに ros2 の rqt が表示される
in-contailner: # rqt 

ちゃんと計測したわけではないが qemu-system でエミュレーションより少し早い気がする。

にしても Claude に言われるがままだな。

参考

Dockerのイメージビルド中でapt-getを高速化するたった1つの方法 | ゲンゾウ用ポストイット
souces.list のミラーを日本の jaist に向ける設定

ruby on rails – Docker build fails because unable to install libc-bin – Stack Overflow
libc-bin のエラー回避

Pi 5 温度の取得とシェルの小ネタ

よく忘れるのでメモ。

前回記事で、Pi5 PoE+ HAT のことを書きましたが、こいつにはファンがついていて温度設定でコントロールできるようになっています。

設定方法は、WaveShare の wiki でみればいいのですが、(多分これ Raspberry 公式と同じ内容なはずです。) ふと温度の確認方法はどうだっけと思い検索しました。

; 
$ vcgencmd measure_temp
#temp=40.1'C

; 普通に cat すると 1000 倍の値が取れる
$ echo  cat /sys/class/thermal/thermal_zone0/temp
47950

;  単純に 1 / 1000 にすると切り捨てられる
$ echo $(($(cat /sys/class/thermal/thermal_zone0/temp) / 1000))
47

; こうすると、小数点以下が生きる
$ cat /sys/class/thermal/thermal_zone0/temp |awk '{print $1 /1000}'
47.95

参考

RaspberryPi(Raspbian)のCPUの温度をcatを使わないで取得してみた #Bash – Qiita

Pi5 に PoE+ HAT で NVME 起動するように設定する

先日から始まった ROS2 関連のエントリになります。

とりあえず ROS2 を Raspberry Pi で動かしているのですが、Pi 4 が 1 台だけだと心もとないので Pi 5 用の POE+ ハットを購入して動かすことにしました。

買ったのは WaveShare の PoE M.2 HAT+ という商品です。日本だと 4,000 から 5,000 円くらいで入手できると思います。自分は WaveShare 公式から買いました。

そんなことはどうでもいいのですが、この HAT には M.2 NVME ソケットがついていて SSD を接続するとそこから Pi 5 を起動できるようになります。というのも ROS2 のモジュールを作る際、ビルドが早い環境があったほうが何かと都合がいいので SSD 起動を試します。

基本、参考セクションの公式のページ通りにやれば何も問題は起きないはずです。が、自分の場合はなぜか pcie デバイスとして認識しない事態になりました。

というのもこの HAT のケーブル向きがあるんです !

写真は最終的に成功したときの様子です。

Pi5 + WaveShare PoE M.2 HAT+ 接続全体

接続がうまく行っているときは右上の赤枠の通り、電源 LED が点灯し、緑のアクセスランプが適宜点灯(要するに点滅)します。

で前述のケーブルの向きですが、こうなります。

PoE M.2 HAT+ ではケーブル向きを上の写真のように三角の位置を揃える必要があるってことです。

これ普通に公式 Wiki 見ていればきづくはずですが、フラットケーブルの端子面は気にしていたのですが、見落としてました。

一般的に片面にしか端子がないフラットケーブルはひっくり返すと通電しません。(コネクタ側も片面しか端子がないので、当たり前です。)

さらに、この HAT の場合には、三角マークの部分で左右非対称になっているようで三角の位置を合わせないとダメでした。つまりこのケーブルは、Pi 5 に挿す側と HAT に挿す側が予め決まっており、裏表も正しく挿す必要がありました。

同様の事態になる人は少ないと思いますが、自戒の意味を込めて晒します。

あとは、まあ公式通りです。

/boot/firmware./config.txt に dtparam の行を追加します。

$ sudo vi /boot/firmware/config.txt
[all]
dtparam=pciex1
dtparam=pciex1_gen=3
:wq

動画かなにかで [cm4] セクションに書いている例もありましが、なんとなく気持ち悪いので [all] セクションに書きました。正直無名セクションや、pi0 系のセクション以外ならで良いとおもいますが。。。

再起動して lspci で SM2263 が表示されれば OKです。

次に rpi-eeprom-config –edit でブートオーダーを変更します。

$ sudo rpi-eeprom-config -edit
BOOT_ORDER=0xf416

あとは、”SD Card Copier” でコピーするなり、”Raspberry Pi Imager” で SSD にOSを書き込めば起動します。

参考

PoE M.2 HAT+ – Waveshare Wiki

Raspberry Pi 5をNVMe SSD起動でデスクトップPC化

Ubuntu 22.04 で Unable update “Snap Store”: といわれアップデートできない

先週から ROS2 をラズパイ 4 で動かし始めています、ROS2 の対応 OS が Ubuntu 22.04 なので RaspberryOS を諦めて素直に U22 にしています。

同じ Debian 系とはいえ普段使っている Linux Mint とも RaspberryOS ともビミョーに異なり、困らないもののストレスフルな毎日です。

表題ですが Snap Store (Ubuntu Software) で Snap Store のアップデートがあるので更新しようとしてもエラーメッセージ「Unable update “Snap Store”: snap … 」とでてアップデートできません。試しに再起動を試みましたが現象変わらず。

以下のコマンドで回避できました。

$ killall snap-store
$ sudo snap refresh snap-store

Ubuntu は多分 Gnome でつかっているんだけどやっぱり遅くてあまり好きになれない。ROS2 動かす GUI はいらないのだけど、カメラのデータを扱いたいのでぱっと確認しやすい GUI ありのがいいのよね。

参考

How to close the Snap Store to allow Snapd to update it – Project Discussion / Snap Store – Ubuntu Community Hub

DietPi で mdns をアクティブにする

ROCK64 に入れた DietPi だが、外からは .local で応答してくれるが、DietPi の中では、.local の名前解決に失敗していたので以下を追加でインストールした。

$ sudo apt install libnss-mdns

# 名前解決できるか確認してみる
$ ping rock64.local
PING rock64.local (192.168.1.250) 56(84) bytes of data.
64 bytes from 192.168.1.250 (192.168.1.250): icmp_seq=1 ttl=64 time=0.157 ms
64 bytes from 192.168.1.250 (192.168.1.250): icmp_seq=2 ttl=64 time=0.163 ms
....

無事に、.local 名を名前解決できるようになった

DietPi で SBC の LED 設定を変更する

小ネタです。

自宅で動いている ROCK64 という SBC (Single Board Computer) では DietPi という軽い SBC 向けディストリビューションを動かしています。

SBC でよくあるように ROCK64 にも LED がいくつかあり自分の状態を表示しています。

DietPi  + ROCK64 の場合は、led-1 (電源コネクタと反対側の端) が動作中はずっとハートビートする設定になっています。設置している場所が寝室なので夜間はかなり煩わしく感じていました。

internet を検索してみると、ブラウザの検索結果の脇で Copilot が教えてくれました。自分で検索結果を読み取るよりよっぽど早かったことには感動。

Copilot によると dietpi-config で設定できるとのこと。実際にやってみると

  1. sudo dietpi-config
  2. 『1: Display Options』
  3. 『 14: LED Control』
  4. 変更したいLEDを選び(今回は led-1)
  5. 表示したい機能を選択する

こんな感じでできる

ラズパイカメラ + nginx で hls(RTMP)サーバ

ここのところカメラでストリーミングの実験をいろいろしているのですが、今回は hls サーバの実験です。RTMPサーバと呼ぶべきなのかもしれませんが何となくあやふやです。

内容的には IoT PLUS さんの内容のほぼまんまですが、ffmpegのパラメータとsystemd での起動に変更したのでさらします。
まあ、誰かの役に立つかもしれないがわからない。

それにしても ffmpeg のコマンドラインは私にとって鬼門です。ドキュメント読む気にイマイチならないのよ。誰か分かりやすい解説ページつくってくれないでしょうか ?

手順

必要な物は、ffmpeg と nginx です。apache は追ってないですが nginx に rtmp モジュールがあってお手軽なのでその例があるのだと思います。

自分の例では http basic auth も設定していないので apache2-utils は不要です。php-fpm も入れません。

インストール

$ sudo apt update
$ sudo apt install ffmpeg nginx libnginx-mod-rtmp

出力フォルダの準備

こちらも丸パクリですが、/dev/shm のリンクを作成します。

$ sudo mkdir -p /var/www/html/live
$ cd /var/www/html/live
$ sudo ln -s /dev/shm hls

index.html の準備

/var/www/html/index.html を作成します。中身はとりあえずパクリです。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>

<body>
  <video id="video" controls width="100%"></video>
  <script>
    if(Hls.isSupported()) {
      var video = document.getElementById('video');
      var hls = new Hls();
      hls.loadSource('live/hls/stream.m3u8');
      hls.attachMedia(video);
      hls.on(Hls.Events.MANIFEST_PARSED,function() {
      video.play();
    });
   }
  </script>
<p>
  iPhoneなどで再生されない場合は、<a href="live/hls/stream.m3u8">こちら</a>をクリック。
</p>
</body>
</html>

nginx の設定

/etc/nginx/rtmp.conf ファイルを作成し、そいつを /etc/nginx/nginx.conf でインクルードしてやります。

もともと nginx.conf のなかで. conf.d の中にある *.conf ファイルは読み込まれるのですが、設定ファイルの位置的にまずいらしくエラーになるのでこの構成にしてます。

更に、/var/www/html のロケーションのハンドラを /etc/nginx/conf.d/default.conf に書きます。

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        allow play all;
        access_log /var/log/nginx/rtmp_access.log;

        application live {
            live on;
            hls on;
            record off;
            hls_path /var/www/html/live/hls;
            hls_fragment 1s;
            hls_type live;
        }
    }
}

これを下の nginx.conf でインクルードしてやります。

events {
----
}
http {
----
}

# http ブロックの外になるように以下の行を追加
include /etc/nginx/rtmp.conf;

/etc/nginx/conf.d/default.conf はこうなります。リッスンポートは 8080 に変更してます。

server {
    listen 8080;
    proxy_set_header   X-Forwarded-For     $proxy_add_x_forwarded_for;
    access_log /var/log/nginx/access.log combined;
    error_log /var/log/nginx/error.log warn;

    location = /favicon.ico {
        access_log off;
        empty_gif;
        expires 30d;
    }

    location / {
        #auth_basic "Web Cam Streaming";
        #auth_basic_user_file /var/www/.htpasswd;

        root /var/www/html;
        index index.html;
        set_real_ip_from    127.0.0.1;
        real_ip_header      X-Forwarded-For;
    }
}

nginx を起動しておきます

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

ffmpeg の起動テスト

一旦、ffmpeg で動画を見れるかテストします。今回の環境ではマイクがないので、音声関連のパラメータはざっくり削除しています。

$ ffmpeg -f v4l2 -thread_queue_size 8192 -s 720x480 -i /dev/video0 \
    -r 15 -c:v h264_omx -b:v 512k \
    -vf "rotate=180*PI/180" \
    -f flv rtmp://localhost/live/stream

パラメータの説明

-s 720×480: 解像度
-r 15: フレームレート(15 フレーム / sec)
-vf “rotate=180*PI/180”: 画像を 180度 ローテーションする。(ラジアンに変換)

すべてがうまくいっていれば、こんなふうにブラウザに動画が表示されます。

systemd の設定

参考にしたページでは起動スクリプトで終わっていましたが、systemd に登録することにします。まずは先ほどの ffmpeg 起動コマンドをスクリプト化します。

さっきは、直打ちしていたパラメータを環境変数に逃がしてあげます。更にちょっと乱暴ですが起動時に ffmpeg のプロセスを一旦全部殺します。ロックファイルで制御するのが丁寧だとおもいますが、面倒なのでよしとします。

#!/bin/bash 

ROTATE=180
BITRATE=512k
FRATE=15
RES=720x480
# RES=1920x1080


# kill other instances of ffmpeg
pkill ffmpeg

# start camera server
ffmpeg -f v4l2 -thread_queue_size 8192 -s $RES -i /dev/video0 -r $FRATE \
    -c:v h264_omx -b:v $BITRATE \
    -vf "rotate=$ROTATE*PI/180" \
    -f flv rtmp://localhost/live/stream > /dev/null 2>&1 </dev/null&

これを呼び出す systemd ユニットを作成します。名前はrtmp-camera.service としましたが好きなものに変えて構いません。

ExecStart に 上のシェルスクリプトのフルパスを指定します。systemd unit ファイルでは フルパスを指定します。そうでないとサービス起動時にエラーになります。

パスはシェルスクリプトを作成した場所に適宜変更してください。

[Unit]
Description=RTMP camera server with ffmpeg.

[Service]
ExecStart=/opt/rtmp/start.sh
Restart=always
#Type=simple
Type=oneshot
RemainAfterExit=yes
User=pi

[Install]
WantedBy=multi-user.target

2024-4-12 Type=oneshot に修正し、RemainAfterExit=yes を追加。

systemctl で有効にしていきます。

$ sudo systemctl enable /opt/rtmp/rtmp-camera.service
$ sudo systemctl daemon-reload
$ sudo systemctl start rtmp-camera.service

daemon-reload はもしかしたら不要かもしれません。とにかく enable した後、ちゃんと起動しているか確かめてください。上手く動いていればブラウザでカメラで撮影している動画が流れているはずです。

参考

超簡単! ffmpeg + rtmp + nginxを使ってraspberry pi + webカメラのストリーミング環境を作る(raspbian buster版) | IoT PLUS

子供の笑顔と笑い声を聞く為に ffmpeg + Nginx + RTMP on RaspberryPI – 長生村本郷Engineers’Blog

【ffmpeg】簡単ネットワークカメラの作り方
ここの例は、hls ではなく jpg 画像を更新し続けているのでクライアントからすると motionJPEG 形式になるのだと思う。