calc_coulomb_fric|動摩擦力の計算(多次元対応、Python関数)

このPython関数は、
クーロン摩擦モデルにもとづき
2つの質点間(質点A⇔質点B)に働く
動摩擦力を計算します。

速度差が0をまたぐときは、
摩擦力の向きが滑らかに反転します。
各軸(x, y, z)方向で動摩擦力が異なる
異方性摩擦も計算できます。

質点Bの入力値を省略すれば、
動かない床や地面との間に働く
動摩擦力として使うこともできます。

1次元の計算だけでなく、
NumPy配列を入力することで
多次元の計算にも自動で対応します。

目次

📋 関数の仕様

⚠️ 単位系について

本関数の入出力は、すべて 一貫した単位系 で入力されることを前提としています。

  • たとえば、SI単位系 [N, m/s] で入力すれば、出力も [N] で返ります
  • 関数内のコメントでは一例としてSI単位系を記載していますが、単位系が統一されていれば他の単位でも計算できます

▶ 入力

  • 動摩擦力 (f_k)
    • 0以上の有限値(摩擦がない方向は0を指定)
  • 質点Aの速度 (v_a)
  • 質点Bの速度 (v_b)
    • 質点Aと同じ座標系で入力
    • 省略した場合は、0(静止)として計算
  • 遷移速度 (v_s)
    • 摩擦力を0から動摩擦力(f_k)まで滑らかに立ち上げる速度幅
    • 0より大きい有限値
    • 省略した場合は、1.0e-6 として計算
  • 多次元の場合は、すべての入力を質点Aの速度(v_a)と同じ要素数(各軸の成分)で指定してください。

▶ 出力

入力した質点Aの速度(v_a)と同じデータ形式(数値、1次元のNumPy配列)で出力します。

  • 質点Aに働く摩擦力 (f_a)
  • 質点Bに働く摩擦力 (f_b)
    • f_a と大きさが同じで、向きが逆(作用・反作用)

▶ 計算方法

  1. 速度差(v_a − v_b)を計算
  2. 速度差を遷移速度(v_s)で割ったベクトルの大きさを計算
    • 動摩擦力が0の成分は、除いて計算
  3. 速度差を動摩擦力で重み付けしたベクトル(速度差 × f_k)の大きさを計算
    • 速度差がない場合や、全成分の動摩擦力が0の場合は、摩擦力は0
  4. 手順2の大きさと1のうち小さい方を、滑らか係数(0〜1)とする
    • 速度差が0を跨ぐときに、摩擦力の向きが急に反転しないようにするため
  5. 「最大散逸の原理」にもとづき、各成分の摩擦力を計算
    • 摩擦力 f = 滑らか係数 × (手順3のベクトル ÷ その大きさ) × f_k
    • 動摩擦力が大きい成分ほど、摩擦力の成分が相対的に大きくなる
      (摩擦力の向きは、速度差の真反対にならない)
  6. 質点Aに働く力として f_a = -f、質点Bに働く力として f_b = +f を出力

具体的な計算方法は、
次のダウンロードエリアで
関数をご確認ください。

📥 ダウンロード

ご自身のコードに組み込む場合は、
以下の関数をコピーしてご使用ください。

⚠️ ご利用にあたって

本関数の利用・改変・再配布条件、および免責事項については、プライバシーポリシー・免責事項 をご確認ください。

▶ 計算関数

入力値が数値・1次元のNumPy配列の場合は、この関数単体で動作します。

  • 入力値がリスト・タプルの場合や、入力値のエラーチェックを追加したい場合は、後述の内部関数もコピーしてご使用ください。

インポートするライブラリ

# NumPy配列(ndarray)を入力する場合
import numpy as np                                      # 配列計算に使用するライブラリ

関数

def calc_coulomb_fric(f_k, v_a, v_b=None, v_s=None):

    """
    クーロン摩擦モデルに基づく摩擦力の計算(多次元・成分別パラメータ対応)
    各成分(x, y, z)で異なる動摩擦力を持つ「異方性摩擦」を、接触力学の「最大散逸の原理」に基づいて計算する
    Args:
        f_k : float | tuple[float] | list[float] | ndarray         動摩擦力 [N] (>= 0, 有限値)
        v_a : float | tuple[float] | list[float] | ndarray         質点Aの速度 [m/s](有限値)
        v_b : float | tuple[float] | list[float] | ndarray | None  質点Bの速度 [m/s](有限値、省略時は0)
        v_s : float | tuple[float] | list[float] | ndarray | None  遷移速度 [m/s] (> 0, 有限値、省略時は1.0e-6)
        ※ ndarray は1次元配列(例:np.array([1.0, 2.0]))で指定してください。
        ※ 各要素が各成分(x, y, z)に対応します。
        ※ 0次元配列(例:np.array(1.0))はスカラーとして扱います。
        ※ 本関数のみを単体で使用する場合は、float または ndarray のみ対応します。
        ※ list・tuple を使う場合や、入力値のエラーチェックを行う場合は、次の内部関数も必要です。
           ・_to_array_1dsim_com        : 入力値(数値・list・tuple・ndarray)を1次元ndarrayに変換
           ・_validate_values_1dsim_com : 配列内の各要素が実数・有限値・指定範囲内であるか確認
           ・_format_output_1dsim_com   : 計算結果の桁あふれを確認し、基準入力と同じ形式に変換
    Returns:
        f_a : float | tuple[float] | list[float] | ndarray  質点Aに働く摩擦力 [N]
        f_b : float | tuple[float] | list[float] | ndarray  質点Bに働く摩擦力 [N]
        ※ いずれも v_a と同じ形式(float / tuple / list / ndarray)で返します。
    Raises:
        TypeError  : 各引数の要素が実数でない場合、または各引数に数値・list・tuple・ndarray 以外を渡した場合
        ValueError : f_k が0以上の有限値でない場合、v_a, v_b が有限値でない場合、v_s が正の有限値でない場合、
                     各引数に2次元以上の ndarray を渡した場合、各引数の要素数が不一致・空の場合、
                     または計算結果が桁あふれした場合

    ========================================================
    Copyright (c) 2026 1Dsim.com

    本関数の利用・改変・再配布の条件は、
    1Dsim.com の「プライバシーポリシー・免責事項」
    (https://www.1dsim.com/privacy-policy/)に従います。
    このCopyright表記は削除しないでください。
    ========================================================
    """

    # エラーメッセージの先頭に表示する関数名を定義
    func_name = "calc_coulomb_fric"

    # 省略された引数のデフォルト値を定義
    v_b_default = 0.0     # 質点Bの速度のデフォルト値(静止) [m/s]
    v_s_default = 1.0e-6  # 遷移速度のデフォルト値 [m/s]

    # 1次元ndarray(またはスカラー値)同士の内積を計算するローカル関数
    def _dot(a, b):
        # 要素ごとの積を計算します。
        prod = a * b
        # 1次元配列の場合は全成分の和(内積)を返し、スカラー値の場合はそのまま返します。
        return sum(prod) if getattr(prod, "ndim", 0) > 0 else prod

    # --- 1. 計算準備と入力値の妥当性チェック ---
    # 内部関数(_to_array_1dsim_com)が定義されている場合は、入力形式の変換を行います。
    if "_to_array_1dsim_com" in globals():
        # 入力の形式(数値・list・tuple・ndarray)にかかわらず同じ数式で計算できるよう、
        # すべて1次元ndarrayに変換します。
        f_k_calc = _to_array_1dsim_com(f_k, func_name)
        v_a_calc = _to_array_1dsim_com(v_a, func_name)

        # 省略された引数は、定義したデフォルト値で初期化します(v_aの要素数に合わせます)。
        dim      = len(v_a_calc)
        v_b_calc = np.array([v_b_default] * dim, dtype=object) if v_b is None else _to_array_1dsim_com(v_b, func_name)
        v_s_calc = np.array([v_s_default] * dim, dtype=object) if v_s is None else _to_array_1dsim_com(v_s, func_name)

        # 各入力の要素数(成分の数)が一致しているか確認します。
        if not (len(f_k_calc) == dim == len(v_b_calc) == len(v_s_calc)):
            raise ValueError(f"{func_name}: 入力値の要素数が一致していません。")

        # 要素数0(空のリストなど)では計算対象がないため、エラーとします。
        if dim == 0:
            raise ValueError(f"{func_name}: 入力値が空です。")
    else:
        # _to_array_1dsim_com を使用しない場合は、変換を行わず、数値またはndarrayとしてそのまま計算します。
        # (+ 0.0 により、整数型のndarrayが入力された場合でもfloat型に変換し、常に浮動小数点数で計算します)
        f_k_calc = f_k + 0.0
        v_a_calc = v_a + 0.0

        # 省略された引数は、定義したデフォルト値で初期化します。
        v_b_calc = v_b_default if v_b is None else v_b + 0.0
        v_s_calc = v_s_default if v_s is None else v_s + 0.0

    # 内部関数(_validate_values_1dsim_com)が定義されている場合は、入力値の妥当性チェックを行います。
    if "_validate_values_1dsim_com" in globals():
        # 動摩擦力f_kが0以上の実数・有限値であるか確認します。
        _validate_values_1dsim_com(f_k, func_name, "動摩擦力f_k", min_val=0, include_min=True)

        # 速度v_a, v_bが実数・有限値であるか確認します(v_bが省略された場合は除く)。
        for arr in (v_a, v_b):
            if arr is not None:
                _validate_values_1dsim_com(arr, func_name, "速度v_a, v_b")

        # 遷移速度v_sが0より大きい実数・有限値であるか確認します(v_sが省略された場合は除く)。
        if v_s is not None:
            _validate_values_1dsim_com(v_s, func_name, "遷移速度v_s", min_val=0, include_min=False)

    # --- 2. 正規化ノルムの計算 ---
    # 各成分の相対速度(質点Aと質点Bの速度差)を計算します [m/s]
    v_diff = v_a_calc - v_b_calc

    # =====================================================================
    # 最大散逸の原理に基づいて、異方性クーロン摩擦の向きと大きさを計算
    # ※成分ごとに動摩擦力(f_k)が異なる場合、
    #  摩擦力の向きは速度差の真反対にはなりません。
    #  動摩擦力が大きい成分ほど、その方向の摩擦力成分が相対的に大きくなります。
    # =====================================================================

    # 速度差を遷移速度(v_s)で割ったベクトルと、その大きさ(ユークリッドノルム)を計算します [-]
    # 摩擦力が動摩擦領域(最大値)に達しているかを判定する指標です。
    # f_k=0(摩擦なし)の成分は摩擦力を生まないため、(f_k_calc > 0) を掛けてこの判定から除外します。
    v_norm_vec = (f_k_calc > 0) * (v_diff / v_s_calc)
    vs_norm    = _dot(v_norm_vec, v_norm_vec) ** 0.5

    # 動摩擦力で重み付けした速度ベクトル(v_diff * f_k)と、その大きさ(ユークリッドノルム)を計算します [N・m/s]
    # 最大散逸の原理にもとづき各成分の摩擦力を算出するための重み割合を求めるときに、分母となります。
    p_vec  = v_diff * f_k_calc
    norm_p = _dot(p_vec, p_vec) ** 0.5

    # 正規化ノルムの計算で桁あふれ(オーバーフロー)していないか確認します。
    if not (vs_norm < float("inf")) or not (norm_p < float("inf")):
        raise ValueError(f"{func_name}: 計算結果が桁あふれしました。入力値の大きさを確認してください。")

    # --- 3. 摩擦力の計算 ---
    # 摩擦が働く成分に速度差がある場合のみ、摩擦力を計算します(ゼロ除算を防ぎます)。
    if norm_p > 0.0 and vs_norm > 0.0:
        # 速度差が0を跨ぐときに摩擦力の向きが急に反転するのを防ぐため、
        # 速度差の大きさに応じて0〜1で変化する滑らか係数(transition)を計算します [-]
        transition = min(1.0, vs_norm)

        # 重み付き速度ベクトル(p_vec)を基準値(norm_p)で正規化し、
        # 最大散逸の原理にもとづき各成分の摩擦力を算出するための重み割合を求めます [-]
        weight = p_vec / norm_p

        # 動摩擦力(f_k)に重み(weight)を掛けることで、
        # 各成分のf_kで定まる楕円状の摩擦限界に沿うように大きさを調整します。
        # さらに滑らか係数(transition)を掛けることで、
        # 速度差が0を跨ぐときは滑らかに摩擦力が反転するようにします [N]
        f_friction = transition * weight * f_k_calc

        # 質点Aには相対速度を妨げる向きの力を、
        # 質点Bには同じ大きさで逆向きの力(反作用)を与えます [N]
        # (+ 0.0 により、力が0のとき -0.0 ではなく 0.0 を返します)
        f_a = -f_friction + 0.0
        f_b =  f_friction + 0.0
    else:
        # 摩擦が働く成分に速度差がない場合や、全成分の動摩擦力が0の場合は、摩擦力を0とします [N]
        # 入力の要素数(スカラーまたは1次元配列)を維持したまま0にするため、v_norm_vec に 0.0 を掛けます。
        # (+ 0.0 により、-0.0 ではなく 0.0 を返します)
        f_a = v_norm_vec * 0.0 + 0.0
        f_b = v_norm_vec * 0.0 + 0.0

    # --- 4. 出力形式の調整 ---
    # 出力形式は v_a の形式(型)のみで決定します(f_k, v_b, v_s の形式は影響しません)。
    # 内部関数(_format_output_1dsim_com)が定義されている場合は、桁あふれを確認し、v_a と同じ形式に変換して返します。
    if "_format_output_1dsim_com" in globals():
        return _format_output_1dsim_com(v_a, func_name, f_a, f_b)

    # _format_output_1dsim_com を使用しない場合は、計算結果(数値またはndarray)をそのまま返します。
    return f_a, f_b

▶ 内部関数①

計算関数にリスト・タプルを入力したいときは、
1次元のNumPy配列へ変換するために
この関数をコピーしてご使用ください。

  • 計算関数と同じファイル内にコピーするだけで、計算関数に認識されて使用されます。

インポートするライブラリ

import numbers      # 数値型の判定に使用する標準ライブラリ
import numpy as np  # 配列計算に使用するライブラリ

関数

def _to_array_1dsim_com(val, func_name):
    """入力値(数値・list・tuple・ndarray)を、成分ごとに分けた1次元ndarrayに変換する内部関数"""
    # 例:5.0 → array([5.0])、(1.0, 2.0) → array([1.0, 2.0])、np.array([1.0, 2.0]) → array([1.0, 2.0])
    # 上記以外の型(文字列・集合・辞書・range・pandas.Series など)は受け付けません。
    # (受け付けると出力形式を正しく判定できず、結果の一部が失われるため)

    if isinstance(val, np.ndarray):
        # 2次元以上のndarrayは、各要素を成分(x, y, z)として扱えないため受け付けません。
        if val.ndim > 1:
            raise ValueError(f"{func_name}: ndarrayは1次元配列で指定してください(入力は{val.ndim}次元です)。")
        # 0次元のndarray(np.array(1.0) など)は、中の数値を取り出して要素1つのリストにします。
        if val.ndim == 0:
            raw_list = [val.item()]
        else:
            # 1次元のndarrayは、要素を順に並べたリストに取り出します。
            # (tolist() により、各要素は numpy の数値(np.float64 など)ではなく Python の数値になります)
            raw_list = val.tolist()

    # list・tupleは、要素を順に並べたリストに取り出します。
    elif isinstance(val, (list, tuple)):
        # numpyの数値(np.float64 など)が含まれる場合は、Pythonの数値に変換してから格納します。
        raw_list = [x.item() if getattr(x, "ndim", None) == 0 else x for x in val]

    # 数値(スカラー)は要素1つのリストにします。
    elif isinstance(val, numbers.Number):
        # numpyの数値(np.float64 など)は、Pythonの数値に変換してから格納します。
        if getattr(val, "ndim", None) == 0:
            raw_list = [val.item()]
        else:
            raw_list = [val]

    # 上記のいずれにも当てはまらない型はエラーとします。
    else:
        raise TypeError(f"{func_name}: {type(val).__name__}型は入力できません。数値、list、tuple、ndarrayを使用してください。")

    # 各要素の型(boolなど)を保持したまま要素ごとの四則演算ができるよう、object型の1次元ndarrayとして返します。
    return np.array(raw_list, dtype=object)

▶ 内部関数②

計算関数に入力される値が
実数や有限値であるか確認したいときは、
この関数をコピーしてご使用ください。

  • 計算関数と同じファイル内にコピーするだけで、計算関数に認識されて使用されます。

インポートするライブラリ

import numbers      # 数値型の判定に使用する標準ライブラリ

関数

def _validate_values_1dsim_com(arr, func_name, label, min_val=None, include_min=True):
    """配列や数値の各要素が実数・有限値であり、指定された範囲内にあるか確認する内部関数"""
    # 2次元以上のndarrayを除外(_to_array_1dsim_com を使用しない場合の対策)
    if getattr(arr, "ndim", 0) > 1:
        raise ValueError(
            f"{func_name}:"
            f" ndarrayは1次元配列で指定してください(入力は{arr.ndim}次元です)。"
        )
    
    # _to_array_1dsim_com を使用しない場合(スカラー値や0次元ndarrayが渡された場合)でも
    # 同じループで判定できるよう、要素を並べたリストに取り出します。
    raw_vals = arr.tolist() if hasattr(arr, "tolist") else arr
    vals = raw_vals if isinstance(raw_vals, (list, tuple)) else [raw_vals]

    for v_val in vals:
        # list・tuple内に0次元のndarrayやnumpyの数値(np.float64 など)が含まれる場合は、Pythonの数値に変換します。
        if getattr(v_val, "ndim", None) == 0:
            v_val = v_val.item()

        # 各要素が実数であるか確認します。
        # (True/False は 1/0 として計算できてしまうため、bool型は明示的に除外します)
        if isinstance(v_val, bool) or not isinstance(v_val, numbers.Real):
            raise TypeError(f"{func_name}: {label}の各要素は実数で指定してください。")

        # 下限値の指定がない場合は、無限大・NaNのみを除外します。
        # (NaN は大小比較の結果が常に False になるため、この1つの条件で無限大と一緒に検出できます)
        if min_val is None:
            if not (-float("inf") < v_val < float("inf")):
                raise ValueError(f"{func_name}: {label}は有限の値である必要があります。")
        # 下限値を含む場合(min_val 以上)
        elif include_min:
            if not (min_val <= v_val < float("inf")):
                raise ValueError(f"{func_name}: {label}は{min_val}以上の有限の値である必要があります。")
        # 下限値を含まない場合(min_val より大きい)
        else:
            if not (min_val < v_val < float("inf")):
                raise ValueError(f"{func_name}: {label}は{min_val}より大きい有限の値である必要があります。")

▶ 内部関数③

計算結果の桁あふれを確認したり、
リスト・タプルで入力した計算結果を
リスト・タプルで出力したいときは、
この関数をコピーしてご使用ください。

  • 計算関数と同じファイル内にコピーするだけで、計算関数に認識されて使用されます。

インポートするライブラリ

import numpy as np  # 配列計算に使用するライブラリ

関数

def _format_output_1dsim_com(ref_input, func_name, *result_arrays):
    """計算結果の桁あふれを確認し、基準入力(ref_input)と同じ形式に変換して返す内部関数"""
    # _to_array_1dsim_com を使用しない場合(計算結果がスカラー値や0次元ndarrayの場合)でも
    # 同じ処理で変換できるよう、各計算結果を要素のリストに統一します。
    result_lists = []
    for arr in result_arrays:
        raw_vals = arr.tolist() if hasattr(arr, "tolist") else arr
        result_lists.append(raw_vals if isinstance(raw_vals, list) else [raw_vals])

    # 入力値が有限でも、値が極端に大きいと計算の途中で桁あふれ(オーバーフロー)し、
    # 結果が無限大やNaNになるため、エラーとします。
    for vals in result_lists:
        for res_val in vals:
            if not (-float("inf") < res_val < float("inf")):
                raise ValueError(f"{func_name}: 計算結果が桁あふれしました。入力値の大きさを確認してください。")

    # 基準入力がスカラー値(または0次元ndarray)なのに、計算結果が複数要素になっている場合は、
    # 結果の一部が失われてしまうため、エラーとします。
    # (_to_array_1dsim_com を使用しない場合に、スカラー値とndarrayが混在していると、ここで検出されます)
    ref_is_scalar = not isinstance(ref_input, (list, tuple)) and getattr(ref_input, "ndim", 0) != 1
    if ref_is_scalar and any(len(vals) != 1 for vals in result_lists):
        raise ValueError(f"{func_name}: 入力値の要素数が一致していません。")

    # 出力形式は ref_input の形式(型)のみで決定します。
    if isinstance(ref_input, np.ndarray):
        # 0次元のndarrayで入力された場合は、0次元のndarrayで返します。
        if ref_input.ndim == 0:
            res = tuple(np.array(vals[0]) for vals in result_lists)
        else:
            res = tuple(np.array(vals) for vals in result_lists)
    elif isinstance(ref_input, list):
        res = tuple(vals for vals in result_lists)
    elif isinstance(ref_input, tuple):
        res = tuple(tuple(vals) for vals in result_lists)
    else:
        # スカラー値(数値など)で入力された場合
        res = tuple(vals[0] for vals in result_lists)

    return res[0] if len(res) == 1 else res

▶ 使用例

前述の計算関数を使って
動作を確認するためのテストコードです。

1次元も多次元も、
入力の形式に合わせて
正しく計算できていることが分かります。

# ===== 1次元のテスト(スカラー入力) =====
res_1d = calc_coulomb_fric(1.0, 1.0, 0.0, 2.0)
print("1D Result:", res_1d, "\n")

# ===== 多次元のテスト(ndarray入力) =====
res_nd = calc_coulomb_fric(np.array([1.0, 0.2]), np.array([1.0, 1.0]))
print("ndarray Result:", res_nd)

▶ Python関数+使用例

先ほどの関数を
すぐに実行できるようにまとめました。

速度差と摩擦力の関係を描くグラフ付きです。

よろしければご活用ください!

☕ ちょっとした小話

学校の物理の授業で教わる動摩擦力。
物理モデルでは、よく使われます。

動摩擦力を働かせたいときだけでなく、
くっつけたいとき も使われます。

物体同士をくっつけたいとき、
強いばねでつなぐことを
思い浮かべるかもしれません。

2つの物体の相対変位(位置ズレ)が
大切な状況であれば、
強いばね要素でつなげるべきです。

しかし相対変位は重要ではなく、
速度や加速度を一致させたいだけなら、
大きな動摩擦力でくっつけるのが正解です。

さらに多次元の計算になると、
もっと面白い現象が起きます。

摩擦力の向きが、速度の真反対からズレる

という現象です。

たとえばスケートの刃のように、
刃の向き(滑りやすい)と
横向き(滑りにくい)がある場合、
斜めに進むと摩擦力の向きは
進行方向の真逆からズレます。

本関数は、
このような異方性摩擦にも
対応しています。

このあたりのマニアックなお話は、
またいずれ!

🔗 こんな記事も書いてます

  • ここで紹介した関数を使った記事を作成したら追記する予定です。

最後までお読みいただき
ありがとうございます!🙇‍♂️

少しでも参考になりましたら、
SNSでシェアしていただけると
次の記事を作成する励みになります!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次