LoginSignup
2
0

NURFの射撃支援システム作ってみた

Last updated at Posted at 2023-06-19

NURFの弾道が山なりで的に当てづらいのでAtom Matrixでエイミングを支援するシステムを作ってみました

使用するデバイス

M5 Atom Matrix
image.png

小型のM5シリーズなら何でも良いのですが、
画面が必要なくて6軸センサが載っているのでAtom Matrixを使用しました。
6軸センサを使用した機能を使わないなら(後述)Atom liteやM5StickCなど手元にあるものを使えばいいと思います。


GLASS Unit
image.png

透明なガラスに青い光が浮かび上がるクールな奴。
もともと射撃時に距離を測定して表示する方法を考えてて、
ダットサイトが理想的なんだけど作るの無理なんだよな~って思ってた時に見てコレダ!ってなって買いました。


LIDAR07 測距(ToF)モジュール
image.png

Amazonで売ってるToFモジュールの中で12mで4000円くらいとバランスが良かったのでこれを選びました。
残念ながら現在は売り切れです・・・

同じようなモジュールとしてはこちらがいいかもしれません
image.png


M5Stack用エンコーダユニット
image.png

クロスヘアを上下に調整するためにつけました。
調整が終わったら外しちゃっていいかも?

距離を測定する

LIDAR07 測距(ToF)モジュールのライブラリを使用します。

サンプルからコードをつまむとこんな感じで使えます。


#include "DFRobot_LIDAR07.h"

DFRobot_LIDAR07_IIC LIDAR07(Wire);
uint16_t distance = 0;

void setup(void) {

  Wire.begin(26, 32);
  M5.begin(true, false, false);
  LIDAR07.begin();
  LIDAR07.startFilter();
  LIDAR07.startMeasure();
  if (LIDAR07.getValue()) {
    distance = LIDAR07.getDistanceMM();
    Serial.printf("Distance:%6dmm ", LIDAR07.getDistanceMM());
  }

M5StackシリーズのあるあるとしてM5.begin()でI2C(Wire)を初期化すると
不具合が起きたりするのでM5.begin()と別で初期化した方がいいです。

OLEDディスプレイを描画する

公式のサンプルコードを改変してスプライトを利用しています

m5-docs
https://docs.m5stack.com/en/unit/Glass%20Unit

M5Unit-GLASS/examples/Unit_GLASS_M5Atom/Unit_GLASS_M5Atom.ino at main · m5stack/M5Unit-GLASS · GitHub
https://github.com/m5stack/M5Unit-GLASS/blob/main/examples/Unit_GLASS_M5Atom/Unit_GLASS_M5Atom.ino


#include <M5Atom.h>
#include <UNIT_GLASS.h>

M5UnitGLASS display;
LGFX_Sprite sprite(&display);

int width, height;
uint16_t distance = 9999;

void setup(void) {

  Wire.begin(26, 32);
  M5.begin(true, false, false);
  display.init(GPIO_NUM_26, GPIO_NUM_32, 400000u);

  display.setRotation(2);
  width = display.width();
  height = display.height();
  Serial.printf("width=%d,height=%d\n", width, height);

  sprite.createSprite(width, height);

  sprite.fillScreen(TFT_BLACK);

  sprite.setTextSize(1);
  sprite.setCursor(width / 2, height / 2);
  sprite.printf("%dmm",distance);
  sprite.pushSprite(0, 0);
}

これで画面の中央に距離が表示されます。

画面表示の内容を作りこむ

NURFは弾道が山なりなのでグレネードランチャーのサイトをイメージします
image.png

単純に距離に応じた目盛りを表示しても面白くないので、
距離に応じて十字が上下してフロントサイトと合わせて照準できるようにしたいと思いました。

image.png

image.png

全ての描画要素は距離、角度に応じて動かせるようにするためにwidth,heightを使って相対指定にしてあります。
(ただし正確ではない)

あと距離を7セグフォントで表示したかったのですが、
spriteNum.setFont(&fonts::Font7);
&fonts::Font7は文字が大きくてUNIT GLASSの解像度には収まらないので、
数字表示用のspriteに描画してから縮小して合成しています。

// setup()内で実行
  display.setRotation(2);
  width = display.width();
  height = display.height();
  Serial.printf("width=%d,height=%d\n", width, height);

  sprite.createSprite(width, height);
  spriteNum.createSprite(width * 2, height / 2);
  spriteNum.setCursor(0, 0);
  spriteNum.setPivot(width * 2, 0);

// 以下描画処理
// 左右の目盛り
    sprite.drawLine(0, 0, 10, 0, TFT_WHITE);
    sprite.drawLine(0, height * 0.2, 10, height * 0.2, TFT_WHITE);
    sprite.drawLine(0, height * 0.4, 10, height * 0.4, TFT_WHITE);
    sprite.drawLine(0, height * 0.6, 10, height * 0.6, TFT_WHITE);
    sprite.drawLine(0, height * 0.8, 10, height * 0.8, TFT_WHITE);
    sprite.drawLine(0, height, 10, height, TFT_WHITE);

    sprite.drawLine(width, 0, width - 10, 0, TFT_WHITE);
    sprite.drawLine(width, height * 0.2, width - 10, height * 0.2, TFT_WHITE);
    sprite.drawLine(width, height * 0.4, width - 10, height * 0.4, TFT_WHITE);
    sprite.drawLine(width, height * 0.6, width - 10, height * 0.6, TFT_WHITE);
    sprite.drawLine(width, height * 0.8, width - 10, height * 0.8, TFT_WHITE);
    sprite.drawLine(width, height, width, height, TFT_WHITE);

// 中央の十字
    sprite.drawLine(width / 2, height / 2, width / 2, height / 2 + 5, TFT_WHITE);
    sprite.drawLine(width / 2, height / 2, width / 2, height / 2 - 5, TFT_WHITE);
    sprite.drawLine(width / 2, height / 2, width / 2 - 10, height / 2, TFT_WHITE);
    sprite.drawLine(width / 2, height / 2, width / 2 + 10, height / 2, TFT_WHITE);

// 距離の表示
  spriteNum.setTextSize(1);
  spriteNum.setCursor(0, 0);
  spriteNum.setFont(&fonts::Font7);
  spriteNum.print("99.9");
// 距離描画用spriteを縮小してspriteに描画する
  spriteNum.pushRotateZoomWithAA(&sprite, width * 0.9, height * 0.02, 0, 0.35, 0.35);
  sprite.setCursor(width * 0.85, height * 0.05);
  sprite.setFont(&fonts::Font2);
  sprite.print("m");
  sprite.pushSprite(0, 0);

弾道計算をする

NURFはライフリングも無いし割と単純な放物線を描くだろう・・・という事で
放物線の式y=ax^2でザックリと計算しました
典型的なaは重力加速度の9.8のはずですが、
距離を測りながら水平射撃して大体実測に合うように調整しました。

image.png

本来は仰角(グラフは水平発射なので俯角になる)に合わせて+記号を上下させようとおもってたのですが、
結局使わずにyで上下に動かすようにしました。

距離に応じてサイトを上下させる

距離に応じてディスプレイに反映します。


 // 距離を取得
    if (canMeasure) {
      LIDAR07.startMeasure();
      if (LIDAR07.getValue()) {
        distance = LIDAR07.getDistanceMM();
        // Serial.printf("Distance:%6dmm ", LIDAR07.getDistanceMM());
      }
    }
  // 距離に応じた変化を計算y=ax^2(と比率調整)
    int y = 1.0 * pow((float)distance, 2) * 0.00001;
  // 距離に応じたサイトの変化量を計算
  // 5mで250mm下降するので最大とする
  // 250mm時に画面の高さheight*0.7分上に動かす
    int distanceShift = map(y, 0, 250, 0, -height * 0.7);

  // サイトの基準位置
  // xは画面中央yは画面の上から90%の位置を基準とする
  // 頬付けした場合大体その位置にフロントサイトが来る
    int sightX = width / 2;
    int sightY = height * 0.9;

  // sightYにdistanceShiftを足して上下に動かす
    sprite.drawLine(sightX, sightY + distanceShift, sightX - 10, sightY + 5 + distanceShift, TFT_WHITE);
    sprite.drawLine(sightX, sightY + distanceShift, sightX + 10, sightY - 5 + distanceShift, TFT_WHITE);
    sprite.drawLine(sightX, sightY + distanceShift, sightX - 10, sightY - 5 + distanceShift, TFT_WHITE);
    sprite.drawLine(sightX, sightY + distanceShift, sightX + 10, sightY + 5 + distanceShift, TFT_WHITE);

これにより+サイトとフロントサイトを重ねて構えることで適正な俯角を取ることができます。

image.png

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0