レビューを受ける大切さについて

実は私は大学の研究室に所属し、博士論文を書いているのですが、最近その博士論文のレビューを親しい研究者の人にしてもらいました。 自分にとってその人は憧れの先輩であり、実はここ数年はその人に近づくことを目標に努力していました。 最近はあまり会話する機会がなく、そのレビューで久しぶりにお話しました。 

そのレビューで感じたこと。博士課程も終わり間際で、少しはその人に近づけたかとも思っていたのですが、いざお話してみて、壮大な勘違いだと気づきました。 本当に、レベルが違いすぎるな、というのを説に感じました。というのも、耳が痛くなるような鋭い指摘をたくさんいただいて、本当になぜこんなことにも気づけなかったのだろうというミスがたくさん見つかりませんでした。正直、その観点で全然考えられていなかった、みたいな点をたくさんコメントいただき、若干自己否定にも陥りました。

ただ、自己否定だけしていても仕方がないので、いただいた指摘を大切に、論文の修正を進めています。 本当、定期的に外部の人にレビューをしてもらい、自分の作品、あるいは自分自身に相対的な視点を持つことは大事だなと思いました。

Pythonで人工衛星の姿勢運動を可視化する

人工衛星の運動は、剛体の運動としてオイラー運動方程式によって記述することができますが、姿勢表現がクオータニオンだったり角速度だったりでいまいち今どういう運動をしているか直感的にわからなかったりします。この問題を解決するために、衛星の動きをグラフィカルに表現できるように、pythonOpenGL module, pygameを使って表現してみました。

Install

下記のサイトが参考になりそう

https://stackabuse.com/advanced-opengl-in-python-with-pygame-and-pyopengl/

ひとまずOutputだけ

Pythonで下記のようなグラフィックを生成できます。 グラフィックは、姿勢に外乱が加わった際に、三軸姿勢で元に戻す様子です。

f:id:spaquid:20220118162425g:plain
三軸姿勢制御の可視化

コード (一部)

SatelliteやSat_VisualizerなどのClassはどこかで提示しますが、メインのループは下記のようなコードになります。

import numpy as np
from math import *

# from quat import *

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *
from GainSetting import GainSetting

from dynamics import Dynamics
from Satellite import Satellite, Sat_Visualizer
from GyroSetting import GyroSetting
from SttSetting import SttSetting
from Timer import Timer
from PIL import Image
from PIL import ImageOps

axis_verts = (
    (-7.5, 0.0, 0.0),
    (7.5, 0.0, 0.0),
    (0.0, -7.5, 0.0),
    (0.0, 7.5, 0.0),
    (0.0, 0.0, -7.5),
    (0.0, 0.0, 7.5),
)
axes = ((0, 1), (2, 3), (4, 5))
axis_colors = (
    (1.0, 0.0, 0.0),
    (0.0, 1.0, 0.0),
    (0.0, 0.0, 1.0),
)  # Red  # Green  # Blue


"""
       5____________6
       /           /|
      /           / |
    1/__________2/  |
    |           |   |
    |           |   |
    |           |   7
    |           |  /
    |           | /
    0___________3/
"""

cube_verts = (
    (-0.15, -0.2, 0.25),
    (-0.15, 0.2, 0.25),
    (0.15, 0.2, 0.25),
    (0.15, -0.2, 0.25),
    (-0.15, -0.2, -0.25),
    (-0.15, 0.2, -0.25),
    (0.15, 0.2, -0.25),
    (0.15, -0.2, -0.25),
    (-0.15, 0.7, 0.25),
    (0.15, 0.7, 0.25),
    (-0.15, -0.7, 0.25),
    (0.15, -0.7, 0.25),
)

cube_edges = (
    (0, 1),
    (0, 3),
    (0, 4),
    (2, 1),
    (2, 3),
    (2, 6),
    (5, 1),
    (5, 4),
    (5, 6),
    (7, 3),
    (7, 4),
    (7, 6),
    (8, 1),
    (8, 9),
    (9, 2),
    (10, 0),
    (10, 11),
    (11, 3),
)

cube_surfaces = (
    (0, 1, 2, 3),  # Front
    (3, 2, 6, 7),  # Right
    (7, 6, 5, 4),  # Left
    (4, 5, 1, 0),  # Back
    (1, 5, 6, 2),  # Top
    (4, 0, 3, 7),  # Bottom
    (1, 8, 9, 2),  # Sap1
    (0, 10, 11, 3),  # Sap2
)

cube_colors = (
    (1.0, 1.0, 1.0),  # White
    (1.0, 1.0, 1.0),  # White
    (1.0, 1.0, 1.0),  # White
    (0.769, 0.118, 0.227),  # Red
    (1.0, 1.0, 1.0),  # White
    (1.0, 1.0, 1.0),  # White
    (1.0, 1.0, 1.0),  # White
    (1.0, 1.0, 1.0),  # White
    (0.3, 0.835, 0.3),  # Yellow
    (0.3, 0.835, 0.3),  # Yellow
)


def Axis():
    glBegin(GL_LINES)
    for color, axis in zip(axis_colors, axes):
        glColor3fv(color)
        for point in axis:
            glVertex3fv(axis_verts[point])
    glEnd()


def Cube():
    glBegin(GL_QUADS)
    for color, surface in zip(cube_colors, cube_surfaces):
        glColor3fv(color)
        for vertex in surface:
            glVertex3fv(cube_verts[vertex])
    glEnd()

    glBegin(GL_LINES)
    glColor3fv((0.0, 0.0, 0.0))
    for edge in cube_edges:
        for vertex in edge:
            glVertex3fv(cube_verts[vertex])
    glEnd()


def main():
    step = -1
    intvl = 10
    imgs = []

    inertia = np.array([0.5, 0.32, 0.62])
    init_q = -np.array([1, 0.0, 0, 0])
    init_omega = 0.000001 * np.array([1, 1, 1]) * np.pi / 180  # 1 / 3.2])
    init_rw_vel = -0 * np.array([0.35, 0.35, 0.35]) / 0.000734
    k_omega = 0.8 * np.array([[0.5, 0, 0], [0, 0.32, 0], [0, 0, 0.62]])
    kq = 0.006 * np.array([[0.5, 0, 0], [0, 0.32, 0], [0, 0, 0.62]])
    kd_omega = 0.000001 * np.array([[0.5, 0, 0], [0, 0.32, 0], [0, 0, 0.62]])
    kqi = 0.00002 * np.array([[0.5, 0, 0], [0, 0.32, 0], [0, 0, 0.62]])
    gainsetting = GainSetting(kq, kqi, k_omega, kd_omega)
    gyrosetting = GyroSetting(
        0,
        0.3 / 60 / 60 * 3 * np.random.randn(3) * 2 * np.pi / 180,
        0.15 / 60 * 2 * np.pi / 180,
        0.05,
    )
    sttsetting = SttSetting(2 / 3600, 10 / 3600, 0.1)
    dt = 0.0025
    timer = Timer(dt)
    sat1 = Satellite(
        inertia,
        init_q,
        init_omega,
        init_rw_vel,
        timer,
        gainsetting,
        gyrosetting,
        sttsetting,
    )
    time = 0
    calc_timer = 0
    dynamics = Dynamics(dt, sat1)

    pygame.init()
    display = (1000, 818)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    # Using depth test to make sure closer colors are shown over further ones
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LESS)

    # Default view
    glMatrixMode(GL_PROJECTION)
    gluPerspective(40, (display[0] / display[1]), 0.1, 50.0)
    glTranslatef(-0, -1, -8)
    glRotatef(90.0, 0.0, 1.0, 0.0)
    glRotatef(-90.0, 1.0, 0.0, 0.0)
    glRotatef(45.0, 0.0, 0.0, 1.0)

    time = 0

    while True:
        dynamics.update_time()
        sat1.reserve_current_state()
        sat1.update_true_vec()
        sat1.update_observe_vec()
        sat1.estimate_att_by_triad()
        sat1.estimate_att_by_foam()
        sat1.observe_omega()
        sat1.observe_quat()
        sat1.update_rw(dt)
        timer.update_time()
        sat1.calc_torque()
        time = time + dt
        calc_timer += dt
        step += 1

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                vis = Sat_Visualizer(dt, sat1)
                vis.visualize_sat_motion()
                vis.visualize_omega()
                vis.visualize_quat()
                vis.compare_observation()
                return imgs

            if event.type == pygame.KEYDOWN:
                # Rotating about the x axis
                if event.key == pygame.K_DOWN:
                    sat1.omega += np.array([0, 0.3, 0.3 / 3.2])

        x = sat1.q[1]  # -time*0.1
        y = sat1.q[2]  # time*0.1
        z = sat1.q[3]  # np.cos(time*0.1) #0.72-time*0.01
        w = sat1.q[0]  # +time*0.01

        mat4 = np.array(
            [
                [
                    1 - 2 * y * y - 2 * z * z,
                    2 * x * y - 2 * z * w,
                    2 * x * z + 2 * y * w,
                    0,
                ],
                [
                    2 * x * y + 2 * z * w,
                    1 - 2 * x * x - 2 * z * z,
                    2 * y * z - 2 * x * w,
                    0,
                ],
                [
                    2 * x * z - 2 * y * w,
                    2 * y * z + 2 * x * w,
                    1 - 2 * x * x - 2 * y * y,
                    0,
                ],
                [0, 0, 0, 1],
            ],
            "f",
        )

        glMatrixMode(GL_MODELVIEW)
        glLoadMatrixf(mat4)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        Cube()
        Axis()
        pygame.display.flip()
        if step % intvl != 0:
            continue
        else:
            pad_step = "{0:04d}".format(step)
            savepath = "img/tutorial3_" + pad_step + ".png"

            width = 1000  # glutGet(GLUT_WINDOW_WIDTH)
            height = 800  # glutGet(GLUT_WINDOW_HEIGHT)

            glReadBuffer(GL_FRONT)
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
            data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)

            # image = Image.fromstring("RGBA", (width, height), data)
            image = Image.frombytes("RGBA", (width, height), data)
            image = ImageOps.flip(image)
            # image.save( savepath )
            imgs.append(image)
        # pygame.time.wait(10)

if __name__ == "__main__":
    imgs = main()
    imgs[0].save('gif/no_momentum.gif'
            , save_all=True
            , append_images=imgs[1:]
            , optimize=False
            , duration=100 #40
            , loop=0)
    pygame.quit()
    quit()

こんな感じで運動が見れる

ノイズ対策

人工衛星におけるノイズの苦しみ

人工衛星は、打ち上げ前にたくさんの試験をして動作の確認を行います。組み上げて最初に動作させる際は、非常に多くの不具合に見舞われ、設計どおりに衛星が動作することはなかなかありません。 その不具合の原因の多くに、コンポーネントから発生するノイズが別のコンポーネントに伝わることがあげられるます。今回は、そのノイズに関する基本的な知識をまとめます。

基本的な考え方

人工衛星のような電気回路とコンピューターで動作するシステムに発生するノイズは、伝導ノイズと放射ノイズの二種類があります。伝導ノイズは、回路の配線上を直接つながるノイズであり、放射ノイズは空間中を伝搬するノイズです。人工衛星、特にCubeSatのような超小型衛星は、回路基板が非常に密集して配置されているため、これらのノイズに対する成立性が非常に難しい課題となります。

回路の種類による影響の受けやすさと影響の与え方

人工衛星の中には、信号のデジタル回路、アナログ回路、電源供給を行う電源回路の三種類あります。アナログ回路などは太陽センサーなどのセンサーで用いられることが多く、電源、デジタル回路はどの部分にも使用される。これらはそれぞれ異なるノイズへの影響の与え方、受け方をします。下記のサイトに詳しく示されています。

www.murata.com

伝導ノイズ対策

伝導ノイズ対策では、基本的に回路に入ってくる高周波のノイズを除去するノイズフィルタを構築することになります。そのため、以下のような方法がとられます。
参考: フィルタ

こうすることで、高周波の電力が、熱として消費されるか、グラウンドに逃すことが可能になります、 これらを満たす素子としていかがあげられる。

放射ノイズ対策

放射ノイズは空間を伝搬するため、回路上の対策だけでは防ぐことはできません。そこで、回路を金属のシールドで覆うことで、流入する空間ノイズや、放射する空間ノイズを防ぐことが可能となります。 下記のサイトに大変よくまとまっています。 www.murata.com

CubeSatの通信規格

人工衛星の内部の通信規格

人工衛星内部のコンポ―ネント同士の通信は、コンピューターと同じように、特定の通信、信号規格で通信します。衛星設計においては、それぞれの通信規格の特性を十分把握する必要があるので、今回はそれらについての知識を備忘録的に整理します。

参考文書

新ヒカリモノズ: シリアル通信・UART・RS232C・RS422・RS485とは

RS485・RS422・RS232Cの違いとは|電圧・規格・距離を比較

基本的な通信規格

通信方式には以下の二つに大きく区別されます

  • シリアル通信

    - 一つの伝送路に次々に情報を送る

    - メリット:システムがシンプルになる

  • パラレル通信

    - 複数の伝送路で、同時に情報を送る

    - メリット:データの伝送速度が速い

    - デメリット:実装コスト

そして、シリアル通信の規格には、代表的なものとして以下があります。

  • I2C通信

  • SPI通信

  • UART通信

UARTとは、本来集積回路のICのことをさすが、それを用いたRX, TXでの通信をUARTと呼ぶことが多い。 それぞれの特徴は下記の通り。

I2C

  • マスターが複数、スレーブが複数の状態で通信できる。
  • SCL, SDAの2本線で通信。SDAは送信、受信を同じSDAで行う。同期式 (SCLがクック)。

SPI

  • マスター1に対して、スレーブが複数
  • 3本、あるいは4本の伝送路。送信、受信の配線を分けているため、同時に送受信が可能である。同期式 (SCLがクック)。

UART

  • 1対1の通信
  • GND含め送信、受信の3本で実施。通信速度を決めて、非同期で行う。

CubeSatで多い通信規格

CubeSatで最も多い通信規格はI2Cであるという調査があります。通信伝送路が少なく、多対多の通信ができることが特徴です。一方で、不具合が最も多いといわれています。 その原因として、I2Cには差動仕様がない、ハンドシェイク、コントロールのための独立ラインがない、多くのノードと結合しすぎる傾向にあるなどが考えられています (下記運用)。 f:id:spaquid:20220108091448p:plain 引用: Bouwmeester, Jasper, Martin Langer, and Eberhard Gill. "Survey on the implementation and reliability of CubeSat electrical bus interfaces." CEAS Space Journal 9.2 (2017): 163-173.

RS232C, RS422, RS485

衛星の設計では、上記の単語ではなく、インターフェースがRS422だ、とかそういう単語が使われてます。これらは、UARTに含まれるシリアルインターフェースの規格です。 伝送速度は、現在では10Mbps程度で15m。 RS232Cは一番シンプルで、信号線とグラウンド線による不平衡伝送 (シングルエンド、伝送路一本)。でやっているので、通信速度が遅く、ノイズに弱い。 そこで改良用に出てきたのがRS422で、平衡型のインターフェースでノイズに強くなります。(コモンモードノイズに対して。、グラウンドを通って流れる全線に共通で入るノイズ)。電圧の規定もあり、Highの時5V or 3.3Vを選択できます。 RS485は、複数の機器で通信できる規格である。線を共有し、必要なタイミングのみ、データラインのアクティブな状態になります。

TTL, CMOS, LVTTL, LVDS

インターフェースでは、上記の通信規格以外にも、信号を送る電圧レベルを規格することがあります。TTL, LVTTL, CMOS, LVDSはその例である(正確には電圧レベルを指定する論理回路のことを指す)。 - TTL, LVTTL - 出力側はLOWとして0V - 0.4V以下、HIGHとして2.4V以上TTLなら5V以下、LVTTLは3.3V以下 - 入力側はLOWは0.8V以下、HIGHは2.0V以上 - CMOSは、Vddの入力に対して、HIGHは0.5-0.7Vdd, Low 0.2Vdd - LVDSは差動信号で、3.5mAの定電流により、終端100Ωの電圧差から、350mVの差動信号として送信する。高速データ通信ができる。

組み合わせ, 違い

例えばSPIだと、LVDSと組み合わせて、差動電圧でノイズ耐性を強化することができます。 U232-Cではとは信号規格が異なり、5V以上でLow , -5V以下でHIGH (負論理) だが、LVTTLは前述の通りであり、UART-LVTTLという形で2本の伝送路で使用する通信をLVTTLにする場合とRS232はさらに異なります。

どの規格を使えばいい?

何かコンポーネントを購入し使用する場合、すでに通信の規格が規定されている場合が多いと思われます。CubeSatだとI2Cが多いと言われていますが、前述のとおりI2Cは不具合が比較的多いと言われていてリスクが大きいです。可能であれば、RS422など、差動信号でノイズに強い通信規格にするのが良いと思います。ただし、こちらも送信可能速度や、コンポ間の接合関係が決まってしまうため、それらの要求が厳しければそれに合った通信規格を使う必要があるかと思います。

以上となります。まだまだこの分野、勉強したてなため、誤っていたらご指摘いただけたらと思います。

日曜日、喫茶店に行く

 

 最近雨が多くなってきて、空気がすっきりしなくなってきています。

リモートワークとやらで、憂鬱になる日もこれまであったが、気晴らしの散歩すらやる気を失うようになってしまう。

 

そんな陰鬱とした中、日曜日の今日は少しだけ晴れ間がのぞいていました。

買い物に出かけつつ、前から気になっていた喫茶店へと足を運ぶ。

喫茶ニカイ、噂には聞いていた通りの、趣ある空間でした。

クリームソーダの青と、壁の青。

家の木目の古びた感じと、ポップなロゴやTシャツ。

どこかおとぎ話に出てきそうな、そんな雰囲気が楽しめてよかったです。

 

f:id:spaquid:20210530201230j:plain

喫茶ニカイ、アンティーク家具と壁の色が絶妙