2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Plotly] 3Dラインの太さ・色変化で速度変化を表現 - "速い・遅い"を見てわかる3D軌跡

Posted at

はじめに

diagram_intro_improved.png

3Dライン(軌跡)を描くだけでは「速度の変化」が分かりにくい。

しかし太さ(line width)や色(colorscale)を速度に応じて変化させることで、
どこが速く、どこがゆっくりなのかを一目で理解できるようになる。

ロボット軌道、物体の運動解析、時系列ラインの"勢い"の可視化に最適。

この記事でできること

  • 3Dラインに"速度"を反映した可視化ができる
  • 速度に応じて「太さ」と「色」を変化
  • Colabでそのまま動くサンプルコード
  • 曲線の特徴(加速・減速・急カーブ)を直感的に把握

基本:速度を計算する

3D軌跡データ(x,y,z)があれば、隣接点の差分から速度を計算できる。

import numpy as np

t = np.linspace(0, 4*np.pi, 300)
# 速度変化を持たせるため、振幅を変化させる
amplitude = 1 + 0.5 * np.sin(t * 0.5)
x = np.sin(t) * amplitude
y = np.cos(t) * amplitude
z = t * 0.2

# 速度(点と点の距離)
dx = np.diff(x)
dy = np.diff(y)
dz = np.diff(z)
speed = np.sqrt(dx**2 + dy**2 + dz**2)

# 可視化用に長さを揃える(速度配列は1つ短い)
speed = np.append(speed, speed[-1])

ポイント

  • 速度=隣接点間距離として定義
  • 実データなら (位置の差分) / (時間間隔) にするだけ
  • 最後の点は便宜的に speed[-1] を入れて長さを合わせる

速度を色(colorscale)で表現する

Plotlyは line.color に配列を渡すと、
各点に応じて色を変化させられる。

import plotly.graph_objects as go

fig = go.Figure()

fig.add_trace(go.Scatter3d(
    x=x, y=y, z=z,
    mode="lines",
    line=dict(
        width=6,
        color=speed,           # 速度で色を変える!
        colorscale="Turbo",
        colorbar=dict(title="Speed")
    ),
    name="Speed Line"
))

fig.update_layout(
    title="3D Line with Speed (Color)",
    scene=dict(aspectmode='data')
)

fig.show()

image.png

見えること

  • 速度が速い区間 → 暖色(赤・黄色)
  • 遅い区間 → 寒色(青)
  • カーブの部分が減速している、直線部分で加速している、などが直感的にわかる

速度を太さ(line width)に反映する

Plotlyの line.width はスカラーしか受け取れないため、
細かく分割した"短い線分"を複数描画する方法が必要。

ラインを短いセグメントに分割して太さを変える

fig = go.Figure()

# 速度を正規化(0〜1の範囲に)
speed_norm = (speed - speed.min()) / (speed.max() - speed.min())

for i in range(len(x)-1):
    fig.add_trace(go.Scatter3d(
        x=[x[i], x[i+1]],
        y=[y[i], y[i+1]],
        z=[z[i], z[i+1]],
        mode="lines",
        line=dict(
            width=3 + speed_norm[i]*15,   # 速度で太さを変える(3〜18の範囲)
            color="red"
        ),
        showlegend=False
    ))

fig.update_layout(
    title="3D Line with Speed (Thickness)",
    scene=dict(aspectmode='data')
)

fig.show()

image.png

ポイント

  • 速度を正規化してから太さに反映させることで、変化を強調
  • 太さの範囲を 3〜18 に設定(変化が見えやすい)
  • 速度変化が小さい場合は係数を大きくする

見えること

  • 速い部分 → 太く
  • 遅い部分 → 細く
  • 曲線の"勢い"がはっきり視覚化される

太さ+色のハイブリッド(最高に見やすい)

速度を色と太さの両方で反映すると説得力抜群。

import plotly.express as px

# 速度を正規化してcolormapから色を取得
norm_speed = (speed - speed.min()) / (speed.max() - speed.min())
colors = px.colors.sample_colorscale("Turbo", norm_speed)

fig = go.Figure()

for i in range(len(x)-1):
    fig.add_trace(go.Scatter3d(
        x=[x[i], x[i+1]],
        y=[y[i], y[i+1]],
        z=[z[i], z[i+1]],
        mode="lines",
        line=dict(
            width=3 + norm_speed[i]*15,  # 正規化した速度で太さを変える
            color=colors[i]  # 正規化した速度から色を取得
        ),
        showlegend=False
    ))

# カラーバーを表示するためのダミートレース
fig.add_trace(go.Scatter3d(
    x=[None], y=[None], z=[None],
    mode='markers',
    marker=dict(
        size=0.1,
        color=speed,
        colorscale="Turbo",
        colorbar=dict(title="Speed"),
        showscale=True
    ),
    showlegend=False
))

fig.update_layout(
    title="3D Line with Speed (Color + Thickness)",
    scene=dict(aspectmode='data')
)

fig.show()

image.png

見えること

  • 色+太さで "速度差の特徴" が圧倒的に分かりやすい
  • 曲線の"流れ"が視覚的に掴める
  • カラーバーで速度の数値範囲も確認できる

背景や軸を整えて見やすくする

fig.update_layout(
    scene=dict(
        xaxis=dict(backgroundcolor='rgb(240,240,240)'),
        yaxis=dict(backgroundcolor='rgb(240,240,240)'),
        zaxis=dict(backgroundcolor='rgb(248,248,248)'),
        aspectmode='data'
    )
)

image.png

  • 薄い背景はラインの色変化を際立たせる
  • aspectmode='data' は歪み防止のためおすすめ

トラブルシュート

太さが効かない

→ Scatter3d の場合「セグメント分割」が必須

太さの変化が見えにくい

→ 速度を正規化してから係数を調整

speed_norm = (speed - speed.min()) / (speed.max() - speed.min())
width = 3 + speed_norm * 15  # 係数を大きくする

色と太さの両方を使ったら見づらい

→ opacity や colorscale を調整

Colabで重い

→ セグメント数が多い場合は点数を間引く

# 例:5点ごとにサンプリング
indices = np.arange(0, len(x), 5)
x_sampled = x[indices]
y_sampled = y[indices]
z_sampled = z[indices]

色が飛ぶ

→ colorscale を "Viridis" や "Cividis" に変更

実装例

import numpy as np
import plotly.graph_objects as go
import plotly.express as px

# データ生成(速度変化を持たせる)
t = np.linspace(0, 4*np.pi, 300)
amplitude = 1 + 0.5 * np.sin(t * 0.5)
x = np.sin(t) * amplitude
y = np.cos(t) * amplitude
z = t * 0.2

# 速度計算
dx = np.diff(x)
dy = np.diff(y)
dz = np.diff(z)
speed = np.sqrt(dx**2 + dy**2 + dz**2)
speed = np.append(speed, speed[-1])

# 速度の正規化と色の準備
norm_speed = (speed - speed.min()) / (speed.max() - speed.min())
colors = px.colors.sample_colorscale("Turbo", norm_speed)

# プロット
fig = go.Figure()

for i in range(len(x)-1):
    fig.add_trace(go.Scatter3d(
        x=[x[i], x[i+1]],
        y=[y[i], y[i+1]],
        z=[z[i], z[i+1]],
        mode="lines",
        line=dict(
            width=3 + norm_speed[i]*15,
            color=colors[i]
        ),
        showlegend=False
    ))

# カラーバー用ダミートレース
fig.add_trace(go.Scatter3d(
    x=[None], y=[None], z=[None],
    mode='markers',
    marker=dict(
        size=0.1,
        color=speed,
        colorscale="Turbo",
        colorbar=dict(title="Speed"),
        showscale=True
    ),
    showlegend=False
))

fig.update_layout(
    title="3D Line with Speed Visualization",
    scene=dict(
        xaxis=dict(backgroundcolor='rgb(240,240,240)'),
        yaxis=dict(backgroundcolor='rgb(240,240,240)'),
        zaxis=dict(backgroundcolor='rgb(248,248,248)'),
        aspectmode='data'
    )
)

fig.show()

image.png

まとめ

image.png

速度の大小は、色と線の太さを組み合わせて表現すると理解しやすい。
太さで表す場合は、線をセグメントに分割する必要がある。

色なら単一トレースでは配列を渡すだけで自動に変化でき、複数セグメントでは px.colors.sample_colorscale で事前計算すると便利。

この2つを併用したハイブリッド表示が、速度変化を最も直感的に把握できる。

適用例

  • 移動体軌跡
  • ロボットアームの動作可視化
  • 時系列データの勢い
  • シミュレーション動作の確認

3Dラインの速度表現は、時系列データの理解を大きく前進させる強力なテクニック。

解説動画

参考情報

2
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?