歯車について勉強する3 の履歴(No.12)

更新


工作/歯車について勉強する

歯車について勉強するシリーズ

目次

歯車設計に必要なさまざまな計算を行います。

歯車を描く

LANG: p5js_live

転位歯車について:

  • 現状では転位歯車の中心間距離を正しく計算できていません

内歯車について:

  • インボリュート曲線部分のみで歯形としているため歯数の少ない場合に歯先円まで届かない(実質的な歯先円が大きくなる)
  • フィレットは付けていない

はすば歯車計算(転位係数から中心間距離)

はすば歯車に転位係数を入力して中心間距離を求められます。

3Dプリンタを使う場合は目的の中心間距離に合わせて変則的なモジュールを選ぶことが可能なので、 「目標中心間距離」を入れて「調整後モジュール」を計算する機能も付けました。

基本的には KHK 小原歯車工業さんの 歯車技術資料 にある、
https://www.khkgears.co.jp/gear_technology/pdf/gijutu.pdf#page=23 の計算を行っています。

LANG:p5js_live
const { tan, cos, sin, PI, atan, abs } = Math

// ====================================================== ここからが設定

const titles = ["項目名", "記号", "計算式", "歯車1", "歯車2"]

const definitions = [
  // 入力
  ["mn", "歯垂直モジュール", "m_n", "", [3], 1],
  ["alphan", "歯直角圧力角", "\\alpha_n", "", [20], PI / 180],
  ["beta", "基準円筒ねじれ角", "\\beta", "", [30], PI / 180],
  ["z", "歯数", "z", "", [12, 60], 1],
  ["xn", "歯直角転位係数", "x_n", "", [0.09809, 0], 1], // 出力
  [
    "alphat",
    "正面圧力角",
    "\\alpha_t",
    "\\tan^{-1}\\Big(\\frac{\\tan\\alpha_n}{\\cos\\beta}\\Big)",
    ["?"],
    PI / 180,
  ],
  [
    "invawt",
    "インボリュートαwt",
    "\\mathrm{inv}\\,\\alpha_{wt}",
    "2\\tan\\alpha_n\\big(\\frac{x_{n1}+x_{n2}}{z_1+z_2}\\big)+\\mathrm{inv}\\,\\alpha_t",
    ["?"],
    1,
  ],
  [
    "alphawt",
    "正面噛み合い圧力角",
    "\\alpha_{wt}",
    "\\mathrm{inv}^{-1}\\,(\\mathrm{inv}\\,\\alpha_{wt})",
    ["?"],
    PI / 180,
  ],
  [
    "y",
    "中心距離修正係数",
    "y",
    "\\frac{z_1+z_2}{2\\cos\\beta}\\Big(\\frac{\\cos\\alpha_t}{\\cos\\alpha_{wt}}-1\\Big)",
    ["?"],
    1,
  ],
  [
    "a",
    "中心距離",
    "a",
    "\\Big(\\frac{z_1+z_2}{2\\cos\\beta}+y\\Big)m_n",
    ["?"],
    1,
  ],
  ["d", "基準円直径", "d", "\\frac{zm_n}{\\cos\\beta}", ["?", "?"], 1],
  ["db", "基礎円直径", "d_b", "d\\cos\\alpha_t", ["?", "?"], 1],
  [
    "dw",
    "噛合ピッチ円直径",
    "d_w",
    "\\frac{d_b}{\\cos\\alpha_{wt}}",
    ["?", "?"],
    1,
  ],
  [
    "ha",
    "歯末の丈",
    "h_a",
    "\\begin{aligned}h_{a1}=(1+y-x_{n2})m_n\\\\h_{a2}=(1+y-x_{n1})m_n\\end{aligned}",
    ["?", "?"],
    1,
  ],
  ["h", "歯丈", "h", "\\{2.25+y-(x_{n1}+x_{n2})\\}m_n", ["?"], 1],
  ["da", "歯先円直径", "d_a", "d+2h_a", ["?", "?"], 1],
  ["df", "歯底円直径", "d_f", "d_a-2h", ["?", "?"], 1],
  ["ad", "目標中心距離", "a_d", "", [130], 1],
  ["mm", "調整後モジュール", "m_m", "m_n a_d / a", ["?"], 1],
]

function recalc() {
  const c = controls
  c.alphat = atan(tan(c.alphan) / cos(c.beta))
  c.invawt =
    (2 * tan(c.alphan) * (c.xn1 + c.xn2)) / (c.z1 + c.z2) + inv(c.alphat)
  c.alphawt = inverse_inv(c.invawt)
  c.y =
    ((c.z1 + c.z2) / (2 * cos(c.beta))) * (cos(c.alphat) / cos(c.alphawt) - 1)
  c.a = ((c.z1 + c.z2) / (2 * cos(c.beta)) + c.y) * c.mn
  c.d1 = (c.z1 * c.mn) / cos(c.beta)
  c.d2 = (c.z2 * c.mn) / cos(c.beta)
  c.db1 = c.d1 * cos(c.alphat)
  c.db2 = c.d2 * cos(c.alphat)
  c.dw1 = c.db1 / cos(c.alphawt)
  c.dw2 = c.db2 / cos(c.alphawt)
  c.ha1 = (1 + c.y - c.xn2) * c.mn
  c.ha2 = (1 + c.y - c.xn1) * c.mn
  c.h = (2.25 + c.y - (c.xn1 + c.xn2)) * c.mn
  c.da1 = c.d1 + 2 * c.ha1
  c.da2 = c.d2 + 2 * c.ha2
  c.df1 = c.da1 - 2 * c.h
  c.df2 = c.da2 - 2 * c.h
  c.mm = (c.mn * c.ad) / c.a
}

// インボリュート関数
function inv(x) {
  return tan(x) - x
}

// インボリュート関数の逆関数
function inverse_inv(inva) {
  if (inva == 0) return 0
  if (inva < 0 || 20 < inva) return NaN
  let a = inva < 2.4 ? 1.441 * inva ** (1 / 3) - 0.374 * inva : 1.48
  let a_prev = 2
  for (;;) {
    const tana = tan(a)
    a += (inva - tana + a) / tana ** 2
    if (abs(a_prev - a) < 1e-15) return a
    a_prev = a
  }
}

// UI の値を読み書きするためのオブジェクト
let controls = {}

// HTML 要素を生成するための関数
function elem(tag, html = "", children = [], props = {}, events = {}) {
  const result = document.createElement(tag)
  if (html != "") result.innerHTML = html
  children.forEach((c) => result.appendChild(c))
  Object.entries(props).forEach(([k, v]) => {
    if (typeof v == "string") {
      result[k] = v
    } else {
      Object.entries(v).forEach(([k2, v2]) => (result[k][k2] = v2))
    }
  })
  Object.entries(events).forEach(([k, v]) => result.addEventListener(k, v))
  return result
}

function katex2html(s) {
  return katex.renderToString(s, { throwOnError: false })
}

function definition2html(def) {
  const [name, label, notation, expression, values, scale] = def
  const createControl = (n, v) => {
    let e
    if (expression != "") {
      e = elem("SPAN", v)
      Object.defineProperty(controls, name + n, {
        get: () => e.innerHTML * scale,
        set: (v) => (e.innerHTML = (v / scale).toPrecision(6)),
      })
    } else {
      e = elem(
        "INPUT",
        "",
        [],
        {
          value: String(v),
          style: {
            textAlign: "right",
            paddingRight: "0.2em",
            width: "4em",
          },
        },
        { change: recalc },
      )
      Object.defineProperty(controls, name + n, {
        get: () => e.value * scale,
        set: (v) => (e.value = (v / scale).toPrecision(6)),
      })
    }
    return e
  }
  const values2controls = () =>
    values.length == 1
      ? [
          elem("TD", "", [createControl("", values[0])], {
            colSpan: "2",
            style: { textAlign: "center" },
          }),
        ]
      : [
          elem("TD", "", [createControl("1", values[0])]),
          elem("TD", "", [createControl("2", values[1])]),
        ]
  const backgroundColor = expression != "" ? "#ffe" : "#eff"
  return elem(
    "TR",
    "",
    [
      elem("TD", label),
      elem("TD", katex2html(notation)),
      elem("TD", katex2html(expression)),
      ...values2controls(),
    ],
    { style: { backgroundColor } },
  )
}

function setup() {
  let table = elem(
    "TABLE",
    "",
    [
      elem("TBODY", "", [
        elem(
          "TR",
          "",
          titles.map((s) => elem("TH", s)),
        ),
        ...definitions.map(definition2html),
      ]),
    ],
    { border: "1", cellSpacing: "0", cellPadding: "2" },
  )

  // キャンバスを最小化する
  p.createCanvas(1, 1)
  // DIV を作ってテーブルを追加する
  p.createDiv().elt.appendChild(table)
  // 値を更新する
  recalc()
}

p.setup = setup

はすば歯車設計(中心間距離から転位量)

モジュール、歯数、ねじれ角と中心間距離を入れると、2つの歯車を合わせてどれだけ転位すればよいかを算出できる。

求まった「歯直角転位係数和」を2つに分けてそれぞれの歯車に振り分けることで目的の中心間距離で噛み合う2つの歯車を設計できる。

具体的には KHK 小原歯車工業さんの 歯車技術資料 にある、
https://www.khkgears.co.jp/gear_technology/pdf/gijutu.pdf#page=24 の計算を行っているだけです。

LANG: p5js_live
const { tan, cos, acos, PI, atan, abs } = Math

// ====================================================== ここからが設定

const titles = ["項目名", "記号", "計算式", "歯車1", "歯車2"]

const definitions = [
  // 入力
  ["mn", "歯垂直モジュール", "m_n", "", [3], 1],
  ["alphan", "歯直角圧力角", "\\alpha_n", "", [20], PI / 180],
  ["beta", "基準円筒ねじれ角", "\\beta", "", [30], PI / 180],
  ["z", "歯数", "z", "", [12, 60], 1],
  ["a", "中心距離", "a", "", [125], 1], // 出力
  [
    "alphat",
    "正面圧力角",
    "\\alpha_t",
    "\\tan^{-1}\\Big(\\frac{\\tan\\alpha_n}{\\cos\\beta}\\Big)",
    ["?"],
    PI / 180,
  ],
  [
    "y",
    "中心距離修正係数",
    "y",
    "\\frac{a}{m_n}-\\frac{z_1+z_2}{2\\cos\\beta}",
    ["?"],
    1,
  ],
  [
    "alphawt",
    "正面噛み合い圧力角",
    "\\alpha_{wt}",
    "\\cos^{-1}\\Big(\\frac{\\cos\\alpha_t}{\\frac{2y\\cos\\beta}{z_1+z_2}+1}\\Big)",
    ["?"],
    PI / 180,
  ],
  [
    "sumxn",
    "歯直角転位係数和",
    "x_{n1}+x_{n2}",
    "\\frac{(z_1+z_2)(\\mathrm{inv}\\,\\alpha_{wt}-\\mathrm{inv}\\,\\alpha_t)}{2\\tan\\alpha_n}",
    ["?"],
    1,
  ],
  [
    "sumxnmm",
    "転位の和 (距離)",
    "(x_{n1}+x_{n2})m_n",
    "(x_{n1}+x_{n2})m_n",
    ["?"],
    1,
  ],
]

function recalc() {
  const c = controls
  c.alphat = atan(tan(c.alphan) / cos(c.beta))
  c.y = c.a / c.mn - (c.z1 + c.z2) / (2 * cos(c.beta))
  c.alphawt = acos(
    cos(c.alphat) / ((2 * c.y * cos(c.beta)) / (c.z1 + c.z2) + 1),
  )
  c.sumxn =
    ((c.z1 + c.z2) * (inv(c.alphawt) - inv(c.alphat))) / (2 * tan(c.alphan))
  c.sumxnmm = c.sumxn * c.mn
}

// インボリュート関数
function inv(x) {
  return tan(x) - x
}

// インボリュート関数の逆関数
function inverse_inv(inva) {
  if (inva == 0) return 0
  if (inva < 0 || 20 < inva) return NaN
  let a = inva < 2.4 ? 1.441 * inva ** (1 / 3) - 0.374 * inva : 1.48
  let a_prev = 2
  for (;;) {
    const tana = tan(a)
    a += (inva - tana + a) / tana ** 2
    if (abs(a_prev - a) < 1e-15) return a
    a_prev = a
  }
}

// UI の値を読み書きするためのオブジェクト
let controls = {}

// HTML 要素を生成するための関数
function elem(tag, html = "", children = [], props = {}, events = {}) {
  const result = document.createElement(tag)
  if (html != "") result.innerHTML = html
  children.forEach((c) => result.appendChild(c))
  Object.entries(props).forEach(([k, v]) => {
    if (typeof v == "string") {
      result[k] = v
    } else {
      Object.entries(v).forEach(([k2, v2]) => (result[k][k2] = v2))
    }
  })
  Object.entries(events).forEach(([k, v]) => result.addEventListener(k, v))
  return result
}

function katex2html(s) {
  return katex.renderToString(s, { throwOnError: false })
}

function definition2html(def) {
  const [name, label, notation, expression, values, scale] = def
  const createControl = (n, v) => {
    let e
    if (expression != "") {
      e = elem("SPAN", v)
      Object.defineProperty(controls, name + n, {
        get: () => e.innerHTML * scale,
        set: (v) => (e.innerHTML = (v / scale).toPrecision(6)),
      })
    } else {
      e = elem(
        "INPUT",
        "",
        [],
        {
          value: String(v),
          style: {
            textAlign: "right",
            paddingRight: "0.2em",
            width: "4em",
          },
        },
        { change: recalc },
      )
      Object.defineProperty(controls, name + n, {
        get: () => e.value * scale,
        set: (v) => (e.value = (v / scale).toPrecision(6)),
      })
    }
    return e
  }
  const values2controls = () =>
    values.length == 1
      ? [
          elem("TD", "", [createControl("", values[0])], {
            colSpan: "2",
            style: { textAlign: "center" },
          }),
        ]
      : [
          elem("TD", "", [createControl("1", values[0])]),
          elem("TD", "", [createControl("2", values[1])]),
        ]
  const backgroundColor = expression != "" ? "#ffe" : "#eff"
  return elem(
    "TR",
    "",
    [
      elem("TD", label),
      elem("TD", katex2html(notation)),
      elem("TD", katex2html(expression)),
      ...values2controls(),
    ],
    { style: { backgroundColor } },
  )
}

function setup() {
  let table = elem(
    "TABLE",
    "",
    [
      elem("TBODY", "", [
        elem(
          "TR",
          "",
          titles.map((s) => elem("TH", s)),
        ),
        ...definitions.map(definition2html),
      ]),
    ],
    { border: "1", cellSpacing: "0", cellPadding: "2" },
  )

  // キャンバスを最小化する
  p.createCanvas(1, 1)
  // DIV を作ってテーブルを追加する
  p.createDiv().elt.appendChild(table)
  // 値を更新する
  recalc()
}

p.setup = setup

はすば内歯車計算(転位係数から中心間距離)

はすばの場合に対応するため、
https://www.khkgears.co.jp/gear_technology/pdf/gijutu.pdf#page=19
にあった内容にねじれ角の項目を追加たのが以下のものです。

エラーチェックは、

  • 内歯車の基礎円と歯先円の大小関係
  • インボリュート干渉 = 内歯車の歯先が平歯車の歯元と干渉
  • トロコイド干渉 = 内歯車の歯先が平歯車の歯先と干渉

を見ているつもりです。

普通に設計して内歯車の基礎円が歯先円よりも大きくなった場合、 歯先の丈を短くして歯先円を大きく定義しなおせばかみ合い長が短くはなりますが使えないことはないので、 歯先と歯元の丈も指定できるようにしています。

LANG: p5js_live
const { tan, sin, cos, acos, PI, atan, abs } = Math;

const titles = ["項目名", "記号", "計算式", "外歯車", "内歯車"]

const definitions = [
  // 入力
  ["mn", "歯垂直モジュール", "m_n", "", [3], 1],
  ["alphan", "歯直角圧力角", "\\alpha_n", "", [20], PI / 180],
  ["beta", "基準円筒ねじれ角", "\\beta", "", [0], PI / 180],
  ["z", "歯数", "z", "", [16, 24], 1],
  ["xn", "歯直角転位係数", "x_n", "", [0, 0.516], 1],
  ["ca", "歯先の丈", "c_a", "", [1, 1], 1],
  ["cf", "歯元の丈", "c_f", "", [1.25, 1.25], 1], // 出力
  ["error", "エラーチェック", "-", "-", ["?"], 0],
  [
    "alphat",
    "正面圧力角",
    "\\alpha_t",
    "\\tan^{-1}\\big(\\frac{\\tan\\alpha_n}{\\cos\\beta}\\big)",
    ["?"],
    PI / 180,
  ],
  [
    "invawt",
    "インボリュートαwt",
    "\\mathrm{inv}\\,\\alpha_{wt}",
    "2\\tan\\alpha_n\\big(\\frac{x_{n2}-x_{n1}}{z_2-z_1}\\big)+\\mathrm{inv}\\,\\alpha_t",
    ["?"],
    1,
  ],
  [
    "alphawt",
    "正面噛み合い圧力角",
    "\\alpha_{wt}",
    "\\mathrm{inv}^{-1}\\,(\\mathrm{inv}\\,\\alpha_{wt})",
    ["?"],
    PI / 180,
  ],
  [
    "y",
    "中心距離修正係数",
    "y",
    "\\frac{z_2-z_1}{2\\cos\\beta}\\big(\\frac{\\cos\\alpha_t}{\\cos\\alpha_{wt}}-1\\big)",
    ["?"],
    1,
  ],
  [
    "a",
    "中心距離",
    "a",
    "\\big(\\frac{z_2-z_1}{2\\cos\\beta}+y\\big)m_n",
    ["?"],
    1,
  ],
  ["d", "基準円直径", "d", "\\frac{zm_n}{\\cos\\beta}", ["?", "?"], 1],
  ["db", "基礎円直径", "d_b", "d\\cos\\alpha_t", ["?", "?"], 1],
  [
    "dw",
    "噛合ピッチ円直径",
    "d_w",
    "\\frac{d_b}{\\cos\\alpha_{wt}}",
    ["?", "?"],
    1,
  ],
  [
    "ha",
    "歯末の丈",
    "h_a",
    "\\scriptsize\\begin{aligned}h_{a1}=(c_{a1}+x_{n1})m_n\\\\h_{a2}=(c_{a2}-x_{n2})m_n\\end{aligned}",
    ["?", "?"],
    1,
  ],
  ["h", "歯丈", "h", "(c_a+c_f)m_n", ["?", "?"], 1],
  [
    "da",
    "歯先円直径",
    "d_a",
    "\\scriptsize\\begin{aligned}d_{a1}: d_1+2h_{a1}\\\\d_{a2}: d_2-2h_{a2}\\end{aligned}",
    ["?", "?"],
    1,
  ],
  [
    "df",
    "歯底円直径",
    "d_f",
    "\\scriptsize\\begin{aligned}d_{f1}: d_{a1}-2h_1\\\\d_{f2}: d_{a2}+2h_2\\end{aligned}",
    ["?", "?"],
    1,
  ],
  ["ad", "目標中心距離", "a_d", "", [13], 1],
  ["mm", "調整後モジュール", "m_m", "m_n a_d / a", ["?"], 1],
]

function recalc() {
  const c = controls
  c.alphat = atan(tan(c.alphan) / cos(c.beta))
  c.invawt =
    (2 * tan(c.alphan) * (c.xn2 - c.xn1)) / (c.z2 - c.z1) + inv(c.alphat)
  c.alphawt = inverse_inv(c.invawt)
  c.y =
    ((c.z2 - c.z1) / (2 * cos(c.beta))) * (cos(c.alphat) / cos(c.alphawt) - 1)
  c.a = ((c.z2 - c.z1) / (2 * cos(c.beta)) + c.y) * c.mn
  c.d1 = (c.z1 * c.mn) / cos(c.beta)
  c.d2 = (c.z2 * c.mn) / cos(c.beta)
  c.db1 = c.d1 * cos(c.alphat)
  c.db2 = c.d2 * cos(c.alphat)
  c.dw1 = c.db1 / cos(c.alphawt)
  c.dw2 = c.db2 / cos(c.alphawt)
  c.ha1 = (c.ca1 + c.xn1) * c.mn
  c.ha2 = (c.ca2 - c.xn2) * c.mn
  c.h1 = (c.ca1 + c.cf1) * c.mn
  c.h2 = (c.ca2 + c.cf2) * c.mn
  c.da1 = c.d1 + 2 * c.ha1
  c.da2 = c.d2 - 2 * c.ha2
  c.df1 = c.da1 - 2 * c.h1
  c.df2 = c.da2 + 2 * c.h2
  c.mm = (c.mn * c.ad) / c.a

  // エラーチェック
  let error = ""
  const msg = (s, c) =>
    `<font style="color: ${c}; font-weight: bold">${s}</font><br>`
  if (c.db2 > c.da2) {
    error += msg("基礎円 &gt; 歯先円", "red")
  } else {
    ;() => {}
    if (
      c.z1 <= (2 * (c.ha2 / (c.mn / cos(c.beta)))) / sin(c.alphat) ** 2 ||
      c.z2 <
        (c.z1 ** 2 -
          (4 * (c.ha2 / (c.mn / cos(c.beta))) ** 2) / sin(c.alphat) ** 2) /
          (c.z1 - (2 * (c.ha2 / (c.mn / cos(c.beta)))) / sin(c.alphat) ** 2) /
          2 ||
      c.z1 >= c.z2
    ) {
      error += msg("インボリュート干渉", "red")
    }
    ;(() => {
      let alphaa1 = acos(c.db1 / c.da1)
      let alphaa2 = acos(c.db2 / c.da2)
      let theta1 =
        acos(
          (c.da2 ** 2 / 4 - c.da1 ** 2 / 4 - c.a ** 2) /
            ((2 * c.a * c.da1) / 2),
        ) +
        inv(alphaa1) -
        c.invawt
      let theta2 = acos(
        (c.a ** 2 + c.da2 ** 2 / 4 - c.da1 ** 2 / 4) / ((2 * c.a * c.da2) / 2),
      )
      if ((theta1 * c.z1) / c.z2 + c.invawt - inv(alphaa2) < theta2) {
        error += msg("トロコイド干渉", "red")
      }
    })()
  }
  if (error == "") {
    error = msg("問題なし", "green")
  }
  c.error = error
}

// インボリュート関数
function inv(x) {
  return tan(x) - x
}

// インボリュート関数の逆関数
function inverse_inv(inva) {
  if (inva == 0) return 0
  if (inva < 0 || 20 < inva) return NaN
  let a = inva < 2.4 ? 1.441 * inva ** (1 / 3) - 0.374 * inva : 1.48
  let a_prev = 2
  for (;;) {
    const tana = tan(a)
    a += (inva - tana + a) / tana ** 2
    if (abs(a_prev - a) < 1e-15) return a
    a_prev = a
  }
}

// UI の値を読み書きするためのオブジェクト
let controls = {}

// HTML 要素を生成するための関数
function elem(tag, html = "", children = [], props = {}, events = {}) {
  const result = document.createElement(tag)
  if (html != "") result.innerHTML = html
  children.forEach((c) => result.appendChild(c))
  Object.entries(props).forEach(([k, v]) => {
    if (typeof v == "string") {
      result[k] = v
    } else {
      Object.entries(v).forEach(([k2, v2]) => (result[k][k2] = v2))
    }
  })
  Object.entries(events).forEach(([k, v]) => result.addEventListener(k, v))
  return result
}

function katex2html(s) {
  return katex.renderToString(s, { throwOnError: false })
}

function definition2html(def) {
  const [name, label, notation, expression, values, scale] = def
  const createControl = (n, v) => {
    let e
    if (expression != "") {
      e = elem("SPAN", v)
      Object.defineProperty(controls, name + n, {
        get: () => (scale == 0 ? e.innerHTML : e.innerHTML * scale),
        set: (v) => (e.innerHTML = scale == 0 ? v : (v / scale).toPrecision(6)),
      })
    } else {
      e = elem(
        "INPUT",
        "",
        [],
        {
          value: String(v),
          style: {
            textAlign: "right",
            paddingRight: "0.2em",
            width: "4em",
          },
        },
        { change: recalc },
      )
      Object.defineProperty(controls, name + n, {
        get: () => (scale == 0 ? e.value : e.value * scale),
        set: (v) => (e.value = scale == 0 ? v : (v / scale).toPrecision(6)),
      })
    }
    return e
  }
  const values2controls = () =>
    values.length == 1
      ? [
          elem("TD", "", [createControl("", values[0])], {
            colSpan: "2",
            style: { textAlign: "center" },
          }),
        ]
      : [
          elem("TD", "", [createControl("1", values[0])]),
          elem("TD", "", [createControl("2", values[1])]),
        ]
  const backgroundColor = expression != "" ? "#ffe" : "#eff"
  return elem(
    "TR",
    "",
    [
      elem("TD", label),
      elem("TD", katex2html(notation)),
      elem("TD", katex2html(expression)),
      ...values2controls(),
    ],
    { style: { backgroundColor } },
  )
}

function setup() {
  let table = elem(
    "TABLE",
    "",
    [
      elem("TBODY", "", [
        elem(
          "TR",
          "",
          titles.map((s) => elem("TH", s)),
        ),
        ...definitions.map(definition2html),
      ]),
    ],
    { border: "1", cellSpacing: "0", cellPadding: "2" },
  )

  // キャンバスを最小化する
  p.createCanvas(1, 1)
  // DIV を作ってテーブルを追加する
  p.createDiv().elt.appendChild(table)
  // 値を更新する
  recalc()
}

p.setup = setup

インボリュート干渉、トロコイド干渉について

下図は https://www.khkgears.co.jp/gear_technology/pdf/gijutu.pdf#page=19 より引用

internal_interference.png

上記のスクリプトでは標準歯車で基準円が歯先円を上回ってしまうとエラーになる。 しかし、このエラーは歯先円の方を大きくしてやれば回避できる。

歯元や歯先の丈が標準でない場合にも干渉が生じるかどうかをチェックしたかったので その条件式について調べたところ次の文献を見つけました。

インボリュート内歯歯車の干渉 (荻野 修作)
日本機械学會論文集 21 巻 (1955) 110 号 p. 749-755
https://doi.org/10.1299/kikai1938.21.749

これによると、歯末のたけのモジュールに対する比を $\rho_1,\rho_2$ とした場合のインボルート干渉の起きない条件は、

$$ \begin{cases} \displaystyle z_1>2c_{a2}\cosec^2\alpha\\ \displaystyle z_2\ge \frac{z_1^2-4c_{a2}^2\cosec^2\alpha}{2(z_1-2c_{a2}\cosec^2\alpha)}\\ \displaystyle z_2>z_1 \end{cases} $$

で与えられるそうなのでこれを使いました。→ なんか正しく検出できていないかもしれません・・・

ただし、この式が与える条件は相手の小径歯車の切り下げを考慮していないのかもしれなくて(?)、 切り下げの行われた歯車との組み合わせであれば干渉しないのにも関わらず干渉が生じると表示される場合があるようです(?)
切り下げについて参考: https://www.khkgears.co.jp/gear_technology/basic_guide/KHK356_1.html

例えば歯数12の内歯車と歯数6の外歯車の組み合わせをバックラッシュ 0 で作成して組み合わせてみた下図からは、この組み合わせでインボリュート干渉が起きないことを確認できます。これは歯数6の歯車に切り下げが発生している(歯元のトロコイド曲線が基礎円より上まで来ておりインボリュート領域が本来の長さまで到達していない)ためであり、切り下げを行わなければ小歯車のインボリュート曲線と内歯車の歯先が干渉します。判別式は小歯車の切り下げを考慮していないためにこの「仮想的な干渉」を検出してしまうのだと思います。

internal8.png

インボリュート干渉のエラーが出ても、小歯車側の基礎円が切り下げ開始点よりも小さい場合には、 「本当にダメなのか」を確認する余地はあると思った方が良さそうです???

はすば内歯車計算 (中心距離から転位量)

https://www.khkgears.co.jp/gear_technology/pdf/gijutu.pdf#page=24 の計算をします。

はすばでも使えるよう少し変更しています。

LANG:p5js_live
const { tan, cos, acos, PI, atan, abs } = Math

// ====================================================== ここからが設定

const titles = ["項目名", "記号", "計算式", "歯車1", "歯車2"]

const definitions = [
  // 入力
  ["mn", "歯垂直モジュール", "m_n", "", [3], 1],
  ["alphan", "歯直角圧力角", "\\alpha_n", "", [20], PI / 180],
  ["beta", "基準円筒ねじれ角", "\\beta", "", [0], PI / 180],
  ["z", "歯数", "z", "", [16, 24], 1],
  ["a", "中心距離", "a", "", [13.1683], 1], // 出力
  [
    "alphat",
    "正面圧力角",
    "\\alpha_t",
    "\\tan^{-1}\\Big(\\frac{\\tan\\alpha_n}{\\cos\\beta}\\Big)",
    ["?"],
    PI / 180,
  ],
  [
    "y",
    "中心距離修正係数",
    "y",
    "\\frac{a}{m_n}-\\frac{z_2-z_1}{2\\cos\\beta}",
    ["?"],
    1,
  ],
  [
    "alphawt",
    "正面噛み合い圧力角",
    "\\alpha_{wt}",
    "\\cos^{-1}\\Big(\\frac{\\cos\\alpha_t}{\\frac{2y\\cos\\beta}{z_2-z_1}+1}\\Big)",
    ["?"],
    PI / 180,
  ],
  [
    "diffxn",
    "歯直角転位係数差",
    "x_{n2}-x_{n1}",
    "\\frac{(z_2-z_1)(\\mathrm{inv}\\,\\alpha_{wt}-\\mathrm{inv}\\,\\alpha_t)}{2\\tan\\alpha_n}",
    ["?"],
    1,
  ],
  [
    "diffxnmm",
    "転位の差 (距離)",
    "(x_{n1}+x_{n2})m_n",
    "(x_{n1}+x_{n2})m_n",
    ["?"],
    1,
  ],
]

function recalc() {
  const c = controls
  c.alphat = atan(tan(c.alphan) / cos(c.beta))
  c.y = c.a / c.mn - (c.z2 - c.z1) / (2 * cos(c.beta))
  c.alphawt = acos(
    cos(c.alphat) / ((2 * c.y * cos(c.beta)) / (c.z2 - c.z1) + 1),
  )
  c.diffxn =
    ((c.z2 - c.z1) * (inv(c.alphawt) - inv(c.alphat))) / (2 * tan(c.alphan))
  c.diffxnmm = c.diffxn * c.mn
}

// インボリュート関数
function inv(x) {
  return tan(x) - x
}

// インボリュート関数の逆関数
function inverse_inv(inva) {
  if (inva == 0) return 0
  if (inva < 0 || 20 < inva) return NaN
  let a = inva < 2.4 ? 1.441 * inva ** (1 / 3) - 0.374 * inva : 1.48
  let a_prev = 2
  for (;;) {
    const tana = tan(a)
    a += (inva - tana + a) / tana ** 2
    if (abs(a_prev - a) < 1e-15) return a
    a_prev = a
  }
}

// UI の値を読み書きするためのオブジェクト
let controls = {}

// HTML 要素を生成するための関数
function elem(tag, html = "", children = [], props = {}, events = {}) {
  const result = document.createElement(tag)
  if (html != "") result.innerHTML = html
  children.forEach((c) => result.appendChild(c))
  Object.entries(props).forEach(([k, v]) => {
    if (typeof v == "string") {
      result[k] = v
    } else {
      Object.entries(v).forEach(([k2, v2]) => (result[k][k2] = v2))
    }
  })
  Object.entries(events).forEach(([k, v]) => result.addEventListener(k, v))
  return result
}

function katex2html(s) {
  return katex.renderToString(s, { throwOnError: false })
}

function definition2html(def) {
  const [name, label, notation, expression, values, scale] = def
  const createControl = (n, v) => {
    let e
    if (expression != "") {
      e = elem("SPAN", v)
      Object.defineProperty(controls, name + n, {
        get: () => (scale == 0 ? e.innerHTML : e.innerHTML * scale),
        set: (v) => (e.innerHTML = scale == 0 ? v : (v / scale).toPrecision(6)),
      })
    } else {
      e = elem(
        "INPUT",
        "",
        [],
        {
          value: String(v),
          style: {
            textAlign: "right",
            paddingRight: "0.2em",
            width: "4em",
          },
        },
        { change: recalc },
      )
      Object.defineProperty(controls, name + n, {
        get: () => (scale == 0 ? e.value : e.value * scale),
        set: (v) => (e.value = scale == 0 ? v : (v / scale).toPrecision(6)),
      })
    }
    return e
  }
  const values2controls = () =>
    values.length == 1
      ? [
          elem("TD", "", [createControl("", values[0])], {
            colSpan: "2",
            style: { textAlign: "center" },
          }),
        ]
      : [
          elem("TD", "", [createControl("1", values[0])]),
          elem("TD", "", [createControl("2", values[1])]),
        ]
  const backgroundColor = expression != "" ? "#ffe" : "#eff"
  return elem(
    "TR",
    "",
    [
      elem("TD", label),
      elem("TD", katex2html(notation)),
      elem("TD", katex2html(expression)),
      ...values2controls(),
    ],
    { style: { backgroundColor } },
  )
}

function setup() {
  let table = elem(
    "TABLE",
    "",
    [
      elem("TBODY", "", [
        elem(
          "TR",
          "",
          titles.map((s) => elem("TH", s)),
        ),
        ...definitions.map(definition2html),
      ]),
    ],
    { border: "1", cellSpacing: "0", cellPadding: "2" },
  )

  // キャンバスを最小化する
  p.createCanvas(1, 1)
  // DIV を作ってテーブルを追加する
  p.createDiv().elt.appendChild(table)
  // 値を更新する
  recalc()
}

p.setup = setup

標準かさ歯車設計

https://www.khkgears.co.jp/gear_technology/pdf/gijutu.pdf#page=31 の計算をします。

はすばへの対応が中途半端なままです。

LANG:p5js_live
const { tan, sin, cos, PI, atan, abs } = Math

// ====================================================== ここからが設定

const titles = ["項目名", "記号", "計算式", "外歯車", "内歯車"]

const definitions = [
  // 入力
  ["sigma", "軸角", "\\Sigma", "", [90], PI / 180],
  ["m", "外端正面モジュール", "m", "", [3], 1],
  ["alphan", "歯垂直圧力角", "\\alpha_n", "", [20], PI / 180],
  ["z", "歯数", "z", "", [20, 40], 1],
  ["betam", "中央ねじれ角", "\\beta_m", "", [35], PI / 180],
  [
    "alphat",
    "正面圧力角",
    "\\alpha_t",
    "\\tan^{-1}\\big(\\frac{\\tan\\alpha_n}{\\cos\\beta_m}\\big)",
    ["?"],
    PI / 180,
  ],
  ["d", "基準円直径", "d", "zm", ["?", "?"], 1],
  [
    "delta",
    "基準円錐角",
    "\\begin{aligned}\\delta_1\\\\\\delta_2\\end{aligned}",
    "\\begin{aligned}&\\tan^{-1}\\big({\\scriptsize\\frac{\\sin\\Sigma}{z_2/z_1+\\cos\\Sigma}}\\big)\\\\&\\Sigma-\\delta_1\\end{aligned}",
    ["?", "?"],
    PI / 180,
  ],
  ["r", "円錐距離", "R", "d_2/2\\sin\\delta_2", ["?"], 1],
  ["b", "歯幅", "b", "", [22], 1],
  ["warning", "歯幅警告", "", "b \\le 0.3R\\ \\|\\ b \\le 10m", ["?"], 0],
  ["h", "歯丈", "h", "2.25m", ["?"], 1],
  ["ha", "歯末の丈", "h_a", "1.00m", ["?"], 1],
  ["hf", "歯元の丈", "h_f", "1.25m", ["?"], 1],
  ["thetaf", "歯元角", "\\theta_f", "\\tan^{-1}(h_f/R)", ["?"], PI / 180],
  ["thetaa", "歯末角", "\\theta_a", "\\tan^{^1}(h_a/R)", ["?"], PI / 180],
  [
    "deltaa",
    "歯先円錐角",
    "\\delta_a",
    "\\delta+\\theta_a",
    ["?", "?"],
    PI / 180,
  ],
  [
    "deltaf",
    "歯底円錐角",
    "\\delta_f",
    "\\delta-\\theta_f",
    ["?", "?"],
    PI / 180,
  ],
  ["da", "外端歯先円直径", "d_a", "d+2h_a\\cos\\delta", ["?", "?"], 1],
  [
    "x",
    "頂点から外端歯先",
    "X",
    "R\\cos\\delta-h_a\\sin\\delta",
    ["?", "?"],
    1,
  ],
  [
    "xb",
    "歯先間の軸方向距離",
    "X_b",
    "\\frac{b\\cos\\delta_a}{\\cos\\theta_a}",
    ["?", "?"],
    1,
  ],
  [
    "di",
    "内端歯先円直径",
    "d_i",
    "d_a-\\frac{2b\\sin\\delta_a}{\\cos\\theta_a}",
    ["?", "?"],
    1,
  ],
]

function recalc() {
  const c = controls
  c.alphat = atan(tan(c.alphan) / cos(c.betam))
  c.d1 = c.z1 * c.m
  c.d2 = c.z2 * c.m
  c.delta1 = atan(sin(c.sigma) / (c.z2 / c.z1 + cos(c.sigma)))
  c.delta2 = c.sigma - c.delta1
  c.r = c.d2 / (2 * sin(c.delta2))
  c.warning =
    c.b > 0.3 * c.r && c.b > 10 * c.m
      ? '<b style="color:red">歯幅過大</b>'
      : '<span style="color:green">なし</span>'
  c.h = 2.25 * c.m
  c.ha = 1.0 * c.m
  c.hf = c.h - c.ha
  c.thetaf = atan(c.hf / c.r)
  c.thetaf = atan(c.hf / c.r)
  c.thetaa = atan(c.ha / c.r)
  c.thetaa = atan(c.ha / c.r)
  c.deltaa1 = c.delta1 + c.thetaa
  c.deltaa2 = c.delta2 + c.thetaa
  c.deltaf1 = c.delta1 - c.thetaf
  c.deltaf2 = c.delta2 - c.thetaf
  c.da1 = c.d1 + 2 * c.ha * cos(c.delta1)
  c.da2 = c.d2 + 2 * c.ha * cos(c.delta2)
  c.x1 = c.r * cos(c.delta1) - c.ha * sin(c.delta1)
  c.x2 = c.r * cos(c.delta2) - c.ha * sin(c.delta2)
  c.xb1 = (c.b * cos(c.deltaa1)) / cos(c.thetaa)
  c.xb2 = (c.b * cos(c.deltaa2)) / cos(c.thetaa)
  c.di1 = c.da1 - (2 * c.b * sin(c.deltaa1)) / cos(c.thetaa)
  c.di2 = c.da2 - (2 * c.b * sin(c.deltaa2)) / cos(c.thetaa)
}

// インボリュート関数
function inv(x) {
  return tan(x) - x
}

// インボリュート関数の逆関数
function inverse_inv(inva) {
  if (inva == 0) return 0
  if (inva < 0 || 20 < inva) return NaN
  let a = inva < 2.4 ? 1.441 * inva ** (1 / 3) - 0.374 * inva : 1.48
  let a_prev = 2
  for (;;) {
    const tana = tan(a)
    a += (inva - tana + a) / tana ** 2
    if (abs(a_prev - a) < 1e-15) return a
    a_prev = a
  }
}

// UI の値を読み書きするためのオブジェクト
let controls = {}

// HTML 要素を生成するための関数
function elem(tag, html = "", children = [], props = {}, events = {}) {
  const result = document.createElement(tag)
  if (html != "") result.innerHTML = html
  children.forEach((c) => result.appendChild(c))
  Object.entries(props).forEach(([k, v]) => {
    if (typeof v == "string") {
      result[k] = v
    } else {
      Object.entries(v).forEach(([k2, v2]) => (result[k][k2] = v2))
    }
  })
  Object.entries(events).forEach(([k, v]) => result.addEventListener(k, v))
  return result
}

function katex2html(s) {
  return katex.renderToString(s, { throwOnError: false })
}

function definition2html(def) {
  const [name, label, notation, expression, values, scale] = def
  const createControl = (n, v) => {
    let e
    if (expression != "") {
      e = elem("SPAN", v)
      Object.defineProperty(controls, name + n, {
        get: () => (scale == 0 ? e.innerHTML : e.innerHTML * scale),
        set: (v) => (e.innerHTML = scale == 0 ? v : (v / scale).toPrecision(6)),
      })
    } else {
      e = elem(
        "INPUT",
        "",
        [],
        {
          value: String(v),
          style: {
            textAlign: "right",
            paddingRight: "0.2em",
            width: "4em",
          },
        },
        { change: recalc },
      )
      Object.defineProperty(controls, name + n, {
        get: () => (scale == 0 ? e.value : e.value * scale),
        set: (v) => (e.value = scale == 0 ? v : (v / scale).toPrecision(6)),
      })
    }
    return e
  }
  const values2controls = () =>
    values.length == 1
      ? [
          elem("TD", "", [createControl("", values[0])], {
            colSpan: "2",
            style: { textAlign: "center" },
          }),
        ]
      : [
          elem("TD", "", [createControl("1", values[0])]),
          elem("TD", "", [createControl("2", values[1])]),
        ]
  const backgroundColor = expression != "" ? "#ffe" : "#eff"
  return elem(
    "TR",
    "",
    [
      elem("TD", label),
      elem("TD", katex2html(notation)),
      elem("TD", katex2html(expression)),
      ...values2controls(),
    ],
    { style: { backgroundColor } },
  )
}

function setup() {
  let table = elem(
    "TABLE",
    "",
    [
      elem("TBODY", "", [
        elem(
          "TR",
          "",
          titles.map((s) => elem("TH", s)),
        ),
        ...definitions.map(definition2html),
      ]),
    ],
    { border: "1", cellSpacing: "0", cellPadding: "2" },
  )

  // キャンバスを最小化する
  p.createCanvas(1, 1)
  // DIV を作ってテーブルを追加する
  p.createDiv().elt.appendChild(table)
  // 値を更新する
  recalc()
}

p.setup = setup

ウォームギア設計

LANG:p5js_live
function input(i, label, value, func) {
  p.createSpan(label).position(0, 20 * i + 10);
  myInput = p.createInput(value).position(150, 20 * i + 10);
  if (func) {
    myInput.input(func);
  } else {
    myInput.elt.disabled = true;
  }
  return myInput;
}

const inputs = {};

function recalc() {
  inputs.beta.value(Math.asin(inputs.n.value() * inputs.m.value() / inputs.dp.value())/Math.PI*180)
  inputs.pitch.value(inputs.m.value() * Math.PI);
  inputs.mn.value(
    inputs.m.value() / Math.cos((inputs.beta.value() / 180) * Math.PI)
  );
  inputs.pitchn.value(
    Math.PI * inputs.mn.value()
  );
  inputs.vdp.value(
    inputs.dp.value()/Math.sin(inputs.beta.value()/180*Math.PI)
  )
}

p.setup = () => {
  p.createCanvas(300,240)
  inputs.m = input(1, "歯直角モジュール", 4, recalc);
  inputs.dp = input(2, "基準円直径", 30, recalc);
  inputs.n = input(3, "条数", 1, recalc);
  inputs.pitch = input(5, "歯直角ピッチ", 0);
  inputs.beta = input(6, "ねじれ角", 0);
  inputs.mn = input(7, "正面モジュール", 4);
  inputs.pitchn = input(8, "正面ピッチ", 0);
  inputs.vdp = input(9, "仮想基準円直径", 0)

  recalc();
}

逆インボリュート計算機

LANG: p5js_live
p.setup = () => {
  p.createCanvas(1,1);
  p.createSpan('inv(a): ');
  const invAlpha = p.createInput();
  invAlpha.value(0.1);
  const answer = p.createDiv();
  const handler = () => {
    const inva = Number(invAlpha.value());
    let a = 1.441 * inva ** (1/3) - 0.374*inva;
    if (inva>2) a = 1.48;
    let str = '';
    for (let i = 0; i < 10; i++) {
      str += `&nbsp; &nbsp; ${i} : ${a}<br/>`;
      const tana = Math.tan(a);
      a += (inva - tana + a) / tana ** 2;
    }
    str += `<br>inv(${a}) = ${Math.tan(a) - a}`;
    answer.html(str);
  }
  handler();
  invAlpha.input(handler);
}

Counter: 260 (from 2010/06/03), today: 2, yesterday: 1