HTML5キャンバス上の人工地平線インジケーター

以下に、制御されたオブジェクトの空間位置を視覚化するための珍しいアイデアの1つをHTML5で実装する方法を示します。このコードは、3次元空間での運転をシミュレートするブラウザゲームで使用できます。情報を提示する方法は、サブテリンまたは他の素晴らしいマシンのシミュレーターに焦点を合わせています。







人工地平線の目的と範囲



ここで考慮される狭義の人工地平線は、その動きを制御するために使用される、ローカル垂直に対するオブジェクトの傾きの視覚化です。勾配は、2つのオイラー角度、ロールピッチの値によって定義されます船員は、航空用語「ピッチ」よりも同義語「トリム」を好みます。



人工地平線に関連する(ただし、完全に同義ではありません)ロシア語の用語:「人工地平線」、「飛行コマンド装置」。英語では、「姿勢指標」「人工地平線」または「ジャイロ地平線」という表現が使用されます。



既知の視覚化手法



ピッチアンドロール表示の分野で成功する解決策を見つけるための作業のほとんどは、航空の利益のために行われてきました。これには簡単な説明があります。パイロットは情報をすばやく読む必要があり、スペースの認識に誤りがあると致命的になる恐れがあります。



ロールとピッチの表示の分野で知られているソリューションのほとんどは、航空機のシルエットと特別な背景の使用に基づいています。一般的な機能:



  • 背景は、地平線を表す線によって、天と地を象徴する2つの部分に分割されています。
  • 航空機のシルエットは、背景と色が対照的な、簡略化された背面図です。
  • ロール角度は、シンボリックホライズンラインとシルエットの翼端を結ぶラインの間の角度としてインジケーターによって決定されます(通常、正確な読み取りのために参照スケールが存在します)。
  • ピッチ角は、シルエットの中心にある制御点の位置に応じて、条件付き水平線に垂直なスケールに沿って測定されます。






大量生産で実装されるシステムには、いくつかの一般的なソリューションがあります。



  • 情報は、シルエットと背景の相対的な位置によって提供されます。
  • ロール角度の変化は、背景に対するシルエットの角度の動きに関連しています。
  • ピッチ角の変化は、背景に対するシルエットの線形変位に関連しています。


しかし、望ましい相対運動がいくつかの異なる方法で実現できることを推測するのは難しいことではありません。前世紀の多くの試行錯誤の後、航空の進化は2つの実行可能な組み合わせを残しました:



1。固定されたシルエット、ロールとピッチの背景で動く。使用される名前:「直接表示」、「飛行機から地面までの眺め」、あまり頻繁ではない「自我中心の表示」。







2.ロールに沿ってのみ移動するシルエット、ピッチに沿ってのみ移動する背景。使用される名前:「逆表示」と「地面から飛行機への眺め」、あまり頻繁ではない「地心表示」。







条項2の名前はシステム全体に適用されますが、システムで採用されているロール角度表示の原則のみを反映していることに注意してください。使用されている両方のシステムのピッチ角の表示は、「ストレート」と「エゴセントリック」です。



以下のような既存のフライトシミュレータでは、マイクロソフトフライトシミュレータおよびデジタル戦闘シミュレーター、ディスプレイの両方のタイプは、アクションで見ることができます。



すべての既知のソリューションが上記のパターンに適合するわけではないことに注意してください。指定されたフレームワークを超える例として、発明の2つの特許RU2561311とRU2331848を考えてみましょう



。最初の特許は「ピッチインジケーターとロールインジケーターの高さが間隔を置いた人工地平線」に関するもので、著者はV.I.PutintsevとN.A.Lituevです。特許から。







必要に応じて、元のソースのテキストで指定のデコードと作品の説明を見つけることができます..。全体として、本発明のアイデアは非常に単純です:「地面から飛行機への眺め」のアイデアはロールとピッチの両方で実現されます(完全な「ジオセントリズム」)が、表示は2つの独立したコンポーネントに分けられます。



第2の発明は、より複雑な名前を有する:「宇宙における航空機の位置および制御の論理的表示のための飛行コマンド装置」。特許の著者:PlentsovA.P。およびZakonovaN.A。







回路の名称の説明、デバイスの説明、アナログとの比較、および設計にわずかな違いがある追加の回路が特許に記載されています。



前の発明と共通することの1つは、両方のチャネルのジオセントリズムの概念である。同時に、人工地平線には、既存のモデルと同様に「飛行機のシンボル」が1つしかありませんが、これはもはやシルエットではなく、3次元モデルである「体積モデル」です。ロールモーションが「リバース」表示で実装されたものと類似していることが判明した場合、このデバイスでのピッチングとダイビングはオリジナルに見えます。







実際のディスプレイシステムの設計には、革新を妨げる多くの要因があります。たとえば、保守主義の合理的な理由の1つは、情報を知覚するスキルを含め、オペレーターが習得したスキルの継続性を維持したいという願望です。コンピュータゲームははるかに多くの創造性をもたらすことができるので、ソリューションの比較分析を掘り下げることなく、最も効果的に見える発明を基礎として取り上げます。



ソリューション要件



コードの記述を開始する前に、タスクを定義しましょう。1。A.P.PlentsovとN.A.Zakonovaの発明に基づいてキャンバスを使用して人工地平線インジケーター



描画するdrawAttitude()関数を記述する必要があります。2



。関数はキャンバスのコンテキスト、座標を取得しますインジケーターの中心、度単位のロール角とピッチ角の値、インジケーター面の半径。



3.ピッチ角の値は、マイナス30度からプラス30度までの間隔に制限されます。



4.ロール角度の値は、マイナス45度からプラス45度までの間隔に制限されます。



5.引数の値がpで指定された値を超える場合。3と4の制限は、インジケーターが最も近い許容値を示します。



関数の作成



機能コードには、次の部分が含まれています



。1。入力された値が制限を超えていないかどうかを確認します。



2.角度をラジアンに変換します。



3.インジケーターの半径の値によって「レイアウト」とフォントの特徴的なサイズをスケーリングします。



4.描画コンポーネント:

a)インジケーター本体。

b)レイアウト。

c)ピッチおよびロールスケール。



以下の関数はこの順序で記述されており、その部分はコメントで区切られています。



完全なコード
index.html:



<!DOCTYPE html>
<html>

<head>
  <title>Attitude</title>
  <script src="js/attitude.js"></script>
</head>

<body>
  <canvas id="drawingCanvas" width="640" height="480"></canvas>
</body>

</html>


attitude.js:



window.onload = function () {

    let canvas = document.getElementById("drawingCanvas");
    let context = canvas.getContext("2d");
    
    let run = function () {
        drawAttitude(context, 320, 240, 30 * Math.sin(performance.now() / 2000), 45 * Math.sin(performance.now() / 5000), 200);
    }

    let interval = setInterval(run, 1000 / 60);
};


drawAttitude = function (ctx, centreX, centreY, pitch, roll, radius = 100) {
    //   :
    if (pitch > 30) pitch = 30;
    if (pitch < -30) pitch = -30;

    if (roll > 45) roll = 45;
    if (roll < -45) roll = -45;
    //  :
    roll *= Math.PI / 180;
    pitch *= Math.PI / 180;
    // ""  :
    let vehicleSize = radius * 0.8;
    ctx.font = Math.round(radius / 8) + "px Arial";
    //    :
    ctx.lineWidth = 2;
    ctx.strokeStyle = "Black";
    // :
    ctx.beginPath();
    ctx.arc(centreX, centreY, radius, 0, Math.PI, false);
    ctx.fillStyle = "Maroon";
    ctx.stroke();
    ctx.fill();
    // :
    ctx.beginPath();
    ctx.arc(centreX, centreY, radius, 0, Math.PI, true);
    ctx.fillStyle = "SkyBlue";
    ctx.stroke();
    ctx.fill();
    //"":
    ctx.beginPath();
    //:
    let topSideIsVisible = (pitch >= 0);
    ctx.strokeStyle = topSideIsVisible ? "Orange" : "Brown";
    ctx.fillStyle = topSideIsVisible ? "Yellow" : "Red";
    ctx.lineWidth = 3;
    //
    //  4 ,       ,
    //  :
    ctx.moveTo(centreX, centreY - Math.sin(pitch) * vehicleSize / 2);
    ctx.lineTo(centreX + vehicleSize * Math.cos(roll), centreY + vehicleSize * Math.sin(roll) * Math.cos(pitch));
    ctx.lineTo(centreX, centreY - 2 * Math.sin(pitch) * vehicleSize);
    ctx.lineTo(centreX - vehicleSize * Math.cos(roll), centreY - vehicleSize * Math.sin(roll) * Math.cos(pitch));
    ctx.lineTo(centreX, centreY - Math.sin(pitch) * vehicleSize / 2);
    ctx.stroke();
    ctx.fill();
    // :
    // :
    ctx.beginPath();
    ctx.strokeStyle = "Black";
    ctx.fillStyle = "Black";
    ctx.lineWidth = 1;
    //:
    ctx.fillText(30, centreX - radius * 0.28, centreY - vehicleSize + radius / 20);
    ctx.fillText(20, centreX - radius * 0.28, centreY - vehicleSize * 0.684 + radius / 20);
    ctx.fillText(10, centreX - radius * 0.28, centreY - vehicleSize * 0.348 + radius / 20);
    // - :
    ctx.moveTo(centreX - radius / 10, centreY - vehicleSize);
    ctx.lineTo(centreX + radius / 10, centreY - vehicleSize);
    ctx.stroke();

    ctx.moveTo(centreX - radius / 10, centreY - vehicleSize * 0.684);
    ctx.lineTo(centreX + radius / 10, centreY - vehicleSize * 0.684);
    ctx.stroke();

    ctx.moveTo(centreX - radius / 10, centreY - vehicleSize * 0.348);
    ctx.lineTo(centreX + radius / 10, centreY - vehicleSize * 0.348);
    ctx.stroke();
    // :
    ctx.beginPath();
    ctx.strokeStyle = "White";
    ctx.fillStyle = "White";
    //:
    ctx.fillText(30, centreX - radius * 0.28, centreY + vehicleSize + radius / 20);
    ctx.fillText(20, centreX - radius * 0.28, centreY + vehicleSize * 0.684 + radius / 20);
    ctx.fillText(10, centreX - radius * 0.28, centreY + vehicleSize * 0.348 + radius / 20);
    // - :
    ctx.moveTo(centreX - radius / 10, centreY + vehicleSize);
    ctx.lineTo(centreX + radius / 10, centreY + vehicleSize);
    ctx.stroke();

    ctx.moveTo(centreX - radius / 10, centreY + vehicleSize * 0.684);
    ctx.lineTo(centreX + radius / 10, centreY + vehicleSize * 0.684);
    ctx.stroke();

    ctx.moveTo(centreX - radius / 10, centreY + vehicleSize * 0.348);
    ctx.lineTo(centreX + radius / 10, centreY + vehicleSize * 0.348);
    ctx.stroke();

    // :
    ctx.lineWidth = 2;

    //+-15 :
    ctx.fillText(15, centreX + radius * 0.6, centreY + radius * 0.22);
    ctx.moveTo(centreX + 0.966 * 0.8 * radius, centreY + 0.259 * 0.8 * radius);
    ctx.lineTo(centreX + 0.966 * 0.95 * radius, centreY + 0.259 * 0.95 * radius);

    ctx.fillText(15, centreX - radius * 0.75, centreY + radius * 0.22);
    ctx.moveTo(centreX - 0.966 * 0.8 * radius, centreY + 0.259 * 0.8 * radius);
    ctx.lineTo(centreX - 0.966 * 0.95 * radius, centreY + 0.259 * 0.95 * radius);

    //+-30 :
    ctx.moveTo(centreX + 0.866 * 0.8 * radius, centreY + 0.5 * 0.8 * radius);
    ctx.lineTo(centreX + 0.866 * 0.95 * radius, centreY + 0.5 * 0.95 * radius);

    ctx.moveTo(centreX - 0.866 * 0.8 * radius, centreY + 0.5 * 0.8 * radius);
    ctx.lineTo(centreX - 0.866 * 0.95 * radius, centreY + 0.5 * 0.95 * radius);

    //+-45 :
    ctx.moveTo(centreX + 0.707 * 0.8 * radius, centreY + 0.707 * 0.8 * radius);
    ctx.lineTo(centreX + 0.707 * 0.95 * radius, centreY + 0.707 * 0.95 * radius);

    ctx.moveTo(centreX - 0.707 * 0.8 * radius, centreY + 0.707 * 0.8 * radius);
    ctx.lineTo(centreX - 0.707 * 0.95 * radius, centreY + 0.707 * 0.95 * radius);

    ctx.stroke();
}






最も理解しにくいのは、「レイアウト」を描画するためのコードです。もっと詳しく考えてみましょう。レイアウトとして、矢印の形をした平らな対称図形を使用することにしました。







レイアウトの上面と下面は、輪郭と塗りつぶしの色が異なります。現在のカラースキームの選択は、コードの最初の部分です。

次は、図の輪郭の構築です。



最も難しい作業は、YOZ平面上の図形の頂点の投影の座標を決定することです。これは、三角関数を使用した式が解決するものです。コード内の頂点は、図の番号順にトラバースされます。



コードの大部分は、スケールと署名に専念しています。スケールマークには多くの違いがあります。上と下、左と右、ラベルの有無です。印象的な行数は、各要素の「個別の」コードの記述によるものです。



対応する角度の三角関数は、ロールマークを適用するために使用されます。各ラベルの角度の値は事前にわかっているので、サインとコサインの既製の値がコードに書き込まれます。



ダイナミクスでインジケーターの外観を評価することをお勧めします。新しい関数を使用して、ピッチとロールの振動を表示してみましょう。位置の多様性を最大にするために、インジケーターの限界に対応する振動振幅と周期を異なるものにし、相互に単純にします。







結論



厳密に言えば、ロールとピッチの視覚化に関する上記のコードは、A。P.PlentsovとN.A. Zakonovaの発明に「基づく」指標と呼ばれるべきです。元のスキームからの逸脱は、タスクを簡素化するために行われ、その他は実装を改善するために行われます。



提示された指標は、設計の観点からは理想からほど遠いものです。表示される値の許容される制限は、客観的な基準には最適ではありません。それでも、興味深いテクノロジーデモンストレーターを作成するタスクは解決されたと見なすことができます。



All Articles