歯車について勉強する の履歴(No.2)
更新- 履歴一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- 工作/歯車について勉強する へ行く。
ちょっと思い立って歯車について勉強してみました†
成果物†
スライダーを動かすこといろいろな歯車を描くことができます。
LANG: p5js_live
const sk = sketch;
// スライダーコントロールを作成するユーティリティ
// ラベルと値表示を追加する
function myCreateSlider(label, i, option) {
let x = 20, y = i * 30 + 20;
// ラベル
sk.createSpan(label).position(x, y);
// 値表示
let span = sk.createSpan(option[2]);
span.position(x + 370, y);
// スライダー
let slider = sk.createSlider(...option);
slider.position(x + 60, y);
slider.size(300); // 値が変更されれば表示を更新
slider.input(()=> span.html(slider.value()));
// スライダーを返す
return slider;
}
function myCreateCheckbox(label, i, j = 0) {
let x = j * 100 + 20, y = i * 30 + 20;
// ラベル
sk.createSpan(label).position(x, y);
// チェックボックス
checkbox = sk.createCheckbox();
checkbox.position(x + 60, y);
return checkbox;
}
let d = 0; // 時刻的な変数
const wsize = 640; // ウィンドウサイズ
// 初期化処理
sk.setup = () => {
sk.createCanvas(wsize, wsize);
sliderM = myCreateSlider('スケール', 0, [ 10, 200, 40, 1]);
sliderZA = myCreateSlider('歯数 A', 1, [ 6, 100, 8, 1]);
sliderZB = myCreateSlider('歯数 B', 2, [ 6, 100, 15, 1]);
sliderA = myCreateSlider('圧力角', 3, [ 10, 30, 20, 1]);
sliderS = myCreateSlider('回転速度', 4, [-10, 50, 20, 1]);
checkDk = myCreateCheckbox('歯先円', 5, 0);
checkDp = myCreateCheckbox('基準円', 5, 1);
checkDb = myCreateCheckbox('基礎円', 5, 2);
checkDf = myCreateCheckbox('歯底円', 5, 3);
}
// 描画
sk.draw = () => {
sk.background(220);
sk.stroke('#00c');
sk.strokeWeight(1);
sk.noFill();
const m = sliderM.value();
const za = sliderZA.value();
const zb = sliderZB.value();
const alpha = sliderA.value()/ 360 * 2 * Math.PI;
let gap = 0; // モジュールをほんの少し小さくすると空隙が生じるはず
let dd = (zb % 2) ? Math.PI/zb : 0; // 歯数の偶奇で位相を調整する
draw_gear(wsize/2 - m*za/2, wsize/2, d/za, m - gap/za, za, alpha);
draw_gear(wsize/2 + m*zb/2, wsize/2, -d/zb + dd, m - gap/zb, zb, alpha);
d += 0.001 * sliderS.value();
}
function draw_gear(ox, oy, d, m, z, a = 20 / 360 * 2 * Math.PI) {
let rp = m * z / 2;
let rk = rp + 2.0 * m / 2;
let rf = rp - 2.5 * m / 2;
let rb = rp * Math.cos(a);
sk.drawingContext.setLineDash([5,2,5]);
if(checkDp.checked()) sk.circle(ox,oy,rp*2);
if(checkDk.checked()) sk.circle(ox,oy,rk*2);
if(checkDf.checked()) sk.circle(ox,oy,rf*2);
if(checkDb.checked()) sk.circle(ox,oy,rb*2);
sk.drawingContext.setLineDash([1,1]);
// r からインボリュート角 a を求める
const a0 = Math.tan(Math.acos(rb/rp))-Math.acos(rb/rp);
const r2a = (r) => Math.tan(Math.acos(rb/r) )-Math.acos(rb/r ) - a0;
let p1=[]
let n = 30, r0=Math.max(rb, rf);
for(let j=0; j <= n; j++) {
let rj = r0 + (rk-r0) * j/n;
p1.unshift([rj, r2a(rj)]);
}
if(rf < rb) {
for(let j=0; j <= n; j++) {
let rj = r0 + (rf-r0) * j/n;
p1.push([rj, r2a(rb)]);
}
}
let p = []
if(rb>rf) {
let last_r = rp;
let ff = 0, ao=-Math.PI/z/2;
for(let dp=0; ; dp-=0.01) {
let y = Math.PI*m/4-m*Math.tan(a)+rp*dp
let x = (rp-m)
let r = Math.sqrt(x*x+y*y)
let aa = Math.atan2(y,x)-dp;
if(last_r<r) break;
p.push([r, aa+ao]);
last_r = r;
}
let f = 0;
for(let dp=0; f++ < 200 ; dp+=0.01) {
let y = Math.PI*m/4-m*Math.tan(a)+rp*dp
let x = (rp-m)
let r = Math.sqrt(x*x+y*y)
let aa = Math.atan2(y,x)-dp;
p.unshift([r, aa+ao]);
}
let j=0;
f=0;
for(let i=0; i<p1.length-1; i++) {
if(p[j][0]<p1[i][0]) continue;
while(j<p.length-1 && p[j+1][0]>p1[i][0]) j++;
if(j>=p.length-1)break;
let pa = ((p[j][0]-p1[i][0])*p[j+1][1]+(p1[i][0]-p[j+1][0])*p[j][1])/(p[j][0]-p[j+1][0]);
if(p1[i][1] < pa) f = 1;
if(f && p1[i][1] >= pa) {
// p1.splice(i,p1.length-i,...p.slice(j-1,-1));
p = p.slice(j-1,-1);
break;
}
p1[i][1]=Math.max(p1[i][1], pa)
}
}
p1.unshift([p1[0][0],Math.PI/z*(1/2)])
if(true) {
// fillet
for(let i=0; i<p1.length; i++) {
if(p1[i][0]<rf+m/2) {
p1[i][1] += (-Math.PI/z*1/2-p1[i][1])*((rf+m/2-p1[i][0])/(m/2))**3;
}
}
} else {
p1.push([p1[p1.length-1][0],-Math.PI/z*1/2])
}
for(let j=0; j<z; j++) {
let dd = 2*Math.PI/z*j;
let dd2 = 2*Math.PI/z*(1/2+j);
for(let i=0; i<p1.length-1; i++) {
sk.line(ox+p1[i][0]*Math.cos(d+p1[i][1]+dd), oy+p1[i][0]*Math.sin(d+p1[i][1]+dd),
ox+p1[i+1][0]*Math.cos(d+p1[i+1][1]+dd), oy+p1[i+1][0]*Math.sin(d+p1[i+1][1]+dd));
sk.line(ox+p1[i][0]*Math.cos(d-p1[i][1]+dd2), oy+p1[i][0]*Math.sin(d-p1[i][1]+dd2),
ox+p1[i+1][0]*Math.cos(d-p1[i+1][1]+dd2), oy+p1[i+1][0]*Math.sin(d-p1[i+1][1]+dd2));
}
}
}
歯車の形状†
参考: https://qiita.com/chromia/items/629311346c80dfd0eac7
形状を指定するパラメータ†
歯車は歯のピッチと歯の枚数、そして、歯の肉厚によって形が決まる。
- モジュール (歯のピッチを $\pi$ で割った値) $m$
- 歯の数 $z$
- 圧力角(歯の肉厚を決める) $\alpha$
歯のピッチに $z$ を掛けると円周になるから、歯車の直径 $d$ に対して $\pi m\cdot z=\pi d$ であり、ここから $$mz=d$$ の関係が出る。
基準円とその他の円†
上の $d$ は「基準円」の直径であり、他の直径と区別するため以下では $d_p$ と書く。
- かみ合う2つの歯車は基準円同士が接するように配置する。
- 基準円の上で歯の厚さはピッチのちょうど半分になり、残りの半分が隙間になる。
- 基準円の上で歯の外形線と半径とがなす角が圧力角 $\alpha$ である。
その他に重要な直径として以下がある。
- 歯先円(歯の先端) $d_k=d_p+2m$
- 基礎円(歯の外形と半径とが接する) $d_b=d_p\cos \alpha$
- 歯底円(歯間の底) $d_f=d_p-2.5m$
基礎円の外側の歯形†
基礎円より外側の歯の形状はインボリュート曲線とすることが多いらしい。 この曲線は外形線が基礎円と交わる点を基準とした極座標 $r,\theta$ を用いて、 $$ \theta=\tan a-a\ \ \text{ただし}\ \ a(r)=\cos^{-1}\frac{r_b}r $$ と表される。ここに現れる $$ \mathrm{inv}\,a=\tan a-a $$ はインボリュート関数と呼ばれる。
基準円の内側の歯形†
特にこだわらないのであれば基礎円より下はまっすぐ中心へ下ろせばよいのであるが、より丈夫にする為に歯の根元にフィレットを付けることも多い。フィレットは直径 $d_f+m$ あたりから緩やかに歯底円へ落とす。
歯数の少ない歯車では†
歯数が少ない歯車、具体的には歯数が 18 未満になると、相手の歯数が大きいときに基準円より下で相手の歯先と干渉してしまう。
そこで基準円より下で先端を逃がすために追加する曲線はトロコイド曲線と呼ばれるものだそうで、以下のように求められる。
相手の歯数が大きいほど干渉は大きくなるため、相手の歯数を無限大に飛ばすと、それはもはや歯車ではなくラックとなる。そこで以下ではラック&ピニオンでの状況を考える。ラックでは歯の角度は圧力角 $\alpha$ そのもので、$r_p$ に相当する高さではやはり歯の幅はピッチの半分になる。
ラックの歯先が通る直線はピニオンの中心から見ると $r_p-2m$ の距離にあり、ラックの移動速度はピニオンの半径 $r_p$ の点の円周の速さと同じである。
下図の状況を考えると、ラックの頂点はピニオンの中心と歯形の $r=r_b$ の点とを結ぶ線の上の、中心から $r_p-2m$ の距離の点を通る。この点をピニオンの中心から見た極座標で表すと、$\theta=\alpha, r=r_p-2m$ である。そこからピニオンが $\delta\phi$ 回転すると、ラックの頂点は $r_p\delta\phi$ だけ平行移動する。その点の曲座標は
$$ \theta=\text{atan}\,\frac{(r_p-2m)\sin\alpha+r_p\delta\phi}{r_p-2m} $$ $$ r=\sqrt{((r_p-2m)\sin\alpha+r_p\delta\phi)^2+(r_p-2m)^2} $$ ただしピニオンが回転しているのでピニオンから見た極座標は $$ \theta=\text{atan}\,\frac{(r_p-2m)\sin\alpha+r_p\delta\phi}{r_p-2m}-\delta\phi $$ となる。 $\theta$ がゼロの地点から一旦 $\alpha$ を超えて、また $\alpha$ に戻ってくるところまでを描けばいいはず。
そのようにして求めたのが冒頭の歯車の形状である。