ATOMSwingのtimelapseで星座追尾

## はじめに

この記事はATOMTechのAtomCamをhackしているatomcam_toolsを使ってtimelapseで星座を追尾する話です。



atomcam_toolsに関しては下記を参照してください。

https://github.com/mnakada/atomcam_tools

https://qiita.com/mnakada/items/7d0fbcb6e629e1ddbd0c

https://qiita.com/mnakada/items/5da19a302b0f7521f225

https://qiita.com/mnakada/items/eddbf8b6f0095e279095

https://qiita.com/mnakada/items/00747fdf048f18a787ff

kitazakiさんのatomcam_toolsお試し記事も貼っておきます。

https://qiita.com/kitazaki/items/85a506b9be8718c5b3c3

https://qiita.com/kitazaki/items/8749ad6c0b95ac14b584



## atomcam_toolsのtimelapseについて

内部動作については上の「atomcam_toolsのtimelapseの負荷低減」の中で解説しているので、ここではtimelapseのコマ取りのイベントでの動作について書きます。

toolsのtimelapseの1コマを撮影したあと、ファイルへの書き込みが終わったところで``/media/mmc/timalapse_hook.sh``が呼び出されます。

本当なら撮影前に呼び出したいところなのですが、内部構造的にblockingするわけにはいかないためイベントのタイミングで出されるメッセージを起点にscriptを実行する形になります。

このscriptでSwingのpan/tiltを動かす処理をすると、blockingしているわけでは無いため動いている間に撮影されてしまいうまくいきません。

なので、撮影完了のタイミングでscriptを呼び出して、次の撮影に備える位置にpan/tiltを動かすという仕組みにしました。



##  目的

普通に星が移動している映像をtimelapseで撮るのも綺麗なのですが、『Swingを使って何か面白い映像撮れないかな』という動機で検討を始めてみました。

ということで、地動説を体感できる映像を撮ってみるというテーマで星は移動せずに地面が回転していく映像をとることを目的にしてみます。

勿論その道の人たちが使う赤道儀とかの機材を使えば簡単にできるのではないかと思いますが、ここではSwing単独でやることに意味を見出しています。



## 事前検討

Swingのpan/tilt軸ですが三脚に固定するとして、三脚側からpan軸(0~355°)、tilt軸(0~180°)の順番になっています。tilt軸の0°側は下向きでbodyが邪魔するので実質使えるのは60~180°くらい。

![スクリーンショット 2024-06-29 13.42.15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/eb1d5e94-803c-5daf-f282-babcc93356fd.jpeg)

それを踏まえると、tilt軸は緯度側の固定に使い、pan軸を回転させることで地軸の回転方向に星を追従させる必要があります。

tiltで追従させると追従範囲が足りないのと、地軸に鉛直方向の映像以外はpanと連動しないと追従できないので画角も回転してしまい目的の映像にならないと思われます。

![スクリーンショット 2024-06-29 14.03.40.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/77c8b1b2-b082-d949-dcd1-f878b9bad5f4.jpeg)

ということで、地軸とpan軸を合わせるために画面中心にマーカーを出して、tilt=180°に設定した時に北極星を中心にとらえることで地軸に合わせることとします。この時地軸と地表との距離は北極星までの距離に対して誤差なので無視できます。

![スクリーンショット 2024-06-29 13.43.49.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/5b73c533-fdd8-d832-60ed-b58d7e3f24a1.jpeg)

また、24時間というのは太陽に正対した正午から翌日の正午までの時間なので、この間の地軸での回転角は360°ではなく360°/365.2422日≒1°の公転の分を加算して約361°にする必要があります。撮影する半日だと0.5°なので無視してもいいかもしれないですが一応計算に含めておきます。

![スクリーンショット 2024-06-29 13.45.03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/56191447-aa87-eaa8-4fb0-da5ff03aa120.jpeg)

## 各種設定
準備としてatomcam_toolsの各種設定を済ませておきます。

### WiFi設定

準備している自宅と違う環境に持っていくので、アクセスポイントの情報を追加して、複数のWiFiに対応できるようにしておきます。

WiFi設定は`/media/mmc/wpa_supplicant.conf`に下記のように書いておくことで起動時に読み込まれます。
ssidとpskにSSIDとWiFiパスワードを設定して、priorityで優先順位をつけられます。

```
ctrl_interface=/var/run/wpa_supplicant
update_config=1

network={
  ssid="xxxxxxxx"
  key_mgmt=WPA-PSK
  pairwise=CCMP TKIP
  group=CCMP TKIP WEP104 WEP40
  psk="xxxxxxxx"
  scan_ssid=1
  priority=5
}

network={
  ssid="yyyyyyyy"
  key_mgmt=WPA-PSK
  pairwise=CCMP TKIP
  group=CCMP TKIP WEP104 WEP40
  psk="yyyyyyyy"
  scan_ssid=1
  priority=3
}

network={
  ssid="zzzzzzzzz"
  key_mgmt=WPA-PSK
  pairwise=CCMP TKIP
  group=CCMP TKIP WEP104 WEP40
  psk="zzzzzzzzzz"
  scan_ssid=1
  priority=1
}
```

また、WiFiが切れても再起動しないようにメンテナンス->モニタリングを設定します。
![スクリーンショット 2024-06-29 13.48.07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/497fdb01-cd91-e0a8-e2c6-43b0970d1ba2.jpeg)

Network確認をonにすることで定期的に接続チェックして必要に応じて再接続します。
異常時再起動をoffにしておくことで接続が切れても再起動はしなくなります。

### timelapse設定
タイムラプス設定で下図のように設定します。

![スクリーンショット 2024-07-26 18.58.30.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/d5afcd27-1844-74e6-8818-281311f4cc34.jpeg)

薄暗くなる18:00くらいから翌日の8:00の間のtimelapseを撮るとして14時間、1680、30fpsで56秒の映像を想定しています。

ついでに昼間の空いている時間は画角固定で雲の様子をTimelapseで撮るように設定しています。

### pan/tilt制御

 timelapse eventで呼び出されるscriptを準備していきます。

とりあえず深夜0時に地面が下側に水平になるように計算します。

![スクリーンショット 2024-06-29 13.59.32.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159374/76bc836c-11a6-f7b1-15d3-0aa2459ee811.jpeg)

pan角は0°〜355°の範囲なので355°〜360°の範囲は撮影範囲外の昼間の時間になるようにします。

1恒星日の秒数：365.2422 / 366.2422 * 24 * 60 * 60 をもとに時刻からpan角を求めます。

ということで、ATOMCamのscriptで小数点演算できる環境で低負荷でbusybox内で完結するということでawkを選択（pythonは別process, bcはbusyboxが古くて入っていない、ash+awkでやるくらいならawkだけでいいかという判断です）、実装してみたのが下記になります。

```sh
#!/usr/bin/awk -f

BEGIN {

  # Angle from the Polaris
  tiltAngle = 30;

  # Angles at Midnight 00:00-JST.
  # ex. timellapse range 18:00 - 06:00 -> 180 dgree at 00:00-JST.
  anglesAtMidnight = 180;

  # Normally, NORTH as it is aligned with the Polaris.
  # If you want to take a time-lapse video of the southern sky, specify SOUTH.
  # This is related to the direction of rotation of the pan.
  direction = "NORTH"; # NORTH or SOUTH

  # Allows time-lapse videos of cloud movement to be taken during the day.
  daytimePanAngle = 0;
  daytimeTiltAngle = 0;

  # The following remain unchanged.
  secondsOfsiderealDay = 365.2422 / 366.2422 * 24 * 60 * 60;
  timeOffset = anglesAtMidnight * secondsOfsiderealDay / 360;
  "date +%s" | getline GMT;
  JST = GMT + 9 * 60 * 60;

  if(((JST % 86400) >= 8 * 60 * 60) && ((JST % 86400) < 18 * 60 * 60)) {
    cmd = sprintf("/scripts/cmd move %3.3f %d 1 0", daytimePanAngle, 180 - daytimeTiltAngle);
    cmd | getline res;
    print cmd >> "/tmp/log/timelapse_hook.log";
    print res >> "/tmp/log/timelapse_hook.log";
  } else {
    dir = 1;
    if(direction == "SOUTH") dir = -1;
    pan = (720 + dir * 360 * ((JST - timeOffset) % 86400) / secondsOfsiderealDay) % 360;
    if(pan < 0) pan = 0;
    if(pan > 355) pan = 355;

    # Move the motor 10 degrees first, as it may not move if the movement is too small.
    pan0 = pan - 10;
    if(pan0 < 0) pan0 += 20;
    cmd = sprintf("/scripts/cmd move %3.3f %d 1 1", pan0, 180 - tiltAngle);
    for(i = 0; i < 3; i++) {
      cmd | getline res;
      print cmd >> "/tmp/log/timelapse_hook.log";
      print res >> "/tmp/log/timelapse_hook.log";
      if(res != "" && index(res, "error") == 0) break;
    }

    cmd = sprintf("/scripts/cmd move %3.3f %d 1 0", pan, 180 - tiltAngle);
    for(i = 0; i < 3; i++) {
      cmd | getline res;
      print cmd >> "/tmp/log/timelapse_hook.log";
      print res >> "/tmp/log/timelapse_hook.log";
      if(res != "" && index(res, "error") == 0) break;
    }
  }
}
```

最初の方で5つパラメータを設定しています。

| パラメータ       | 設定内容                                                     | 初期値 |
| ---------------- | ------------------------------------------------------------ | ------ |
| tiltAngle        | 北極星を天頂とした時の北極星からのtilt角度です。<br>ある程度北極星が画面に入る範囲で撮影した方が絵になる気がします。 | 30     |
| anglesAtMidnight | 深夜0時のpan角度です。180°で地面が下に水平になる状態です。<br>355°〜360°が撮影期間に入らないように設定してください。 | 180    |
| direction        | 天頂の方向です。SOUTHにするとpan軸の計算が逆転します。<br>日本では南の軸に合わせることはできないので、北極星で合わせた後に180°反転してカメラを取り付け直すことを想定しています。 | NORTH  |
| daytimePanAngle  | 昼間の空いている時間のPan方向です。 | 0 |
| daytimeTiltAngle | 昼間の空いている時間の北極星からのtilt角度です。 | 0 |

その後、固定値と現在時刻を取得、pan角を計算して一旦pan角から10°ずらした位置に向きを変えた後、pan角に戻します。

これはATOMSwingの特性のようで微妙な移動距離の場合、動くほどの距離でないと判断しているのか暫くの間panが動かず、ある程度してから一気に動くような動作がみられたため、一旦10°ずらす動作を入れています。

for文で3回回しているのはエラー時のリトライ処理です。

### 



## 設置して撮影


夜間
<iframe width="560" height="315" src="https://www.youtube.com/embed/UoHUwm2ddTE?si=h8An0_aCKfpu2T1d" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

昼間
<iframe width="560" height="315" src="https://www.youtube.com/embed/OB5c_snFuaE?si=JzGhTbx3wm9aqooE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

カシオペアと北斗七星など確認できていて、星を固定して地面が動く映像を撮るという目的は達成できました。
~~夏場なので、どうしても大気に水分が多く雲も湧くので冬にちゃんと撮影してみたいです。~~
7/30 ほとんど雲のない映像が撮れました。夜間の映像差し替えました。

でも、昼間の雲の映像の方がダイナミックで面白いですね。

:::note info
今回は山梨県忍野村の忍野スカイスポーツ倶楽部に設置して撮影させてもらいました。ここはハンググライダーの体験飛行も受け付けてます。
週末も仕事のことが頭から離れないエンジニアには空を飛ぶことで全部忘れられるのでオススメです。月曜日に仕事が忘却の彼方に行っているかもしれませんが。
:::


## まとめ

ATOMSwingでatomcam_toolsのtimelapse機能を使って星座の追尾をしてみました。

他にも時刻などで方向を少しずつ変えるtimelapseなど応用はできそうですが、いい例が思いつきませんでした。
何か思いついた方は是非活用してみてください。