рдореИрдВрдиреЗ рд╕реНрдкреЗрд╕ рдЗрдВрдЬреАрдирд┐рдпрд░реНрд╕ рдореЗрдВ рд╣реЗрдХреНрд╕рд╛рдкреЙрдб рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ред рднрд╛рдЧ 1

рдирдорд╕реНрддреЗ рдореИрдВ рд╕реНрдкреЗрд╕ рдЗрдВрдЬреАрдирд┐рдпрд░реНрд╕ рдореЗрдВ рдирд┐рд░реНрдорд┐рдд рд╣реЗрдХреНрд╕рд╛рдкреЙрдб рдореЗрдВ рдПрдХ рдЕрдВрдЧ рдирд┐рдпрдВрддреНрд░рдг рдкреНрд░рдгрд╛рд▓реА рдХреЗ рдбрд┐рдЬрд╛рдЗрди рдФрд░ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред

рдЖрдЧреЗ рджреЗрдЦрддреЗ рд╣реБрдП, рдореИрдВ рдХрд╣реВрдВрдЧрд╛ рдХрд┐ рд╕реНрдкреЗрд╕ рдЗрдВрдЬреАрдирд┐рдпрд░ рдореЗрдВ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╕рдм рдХреБрдЫ рдЕрдЧрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рд╣реЛрдЧрд╛ред рдЗрд╕рдореЗрдВ, рдореИрдВ рд░рд┐рд╡рд░реНрд╕ рдХрд┐рдиреЗрдореИрдЯрд┐рдХреНрд╕ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реВрдВрдЧрд╛ рдФрд░ HTML рдХреИрдирд╡рд╕ рдкрд░ рдПрдХ рдкреНрд░реЛрдЯреЛрдЯрд╛рдЗрдк рджрд┐рдЦрд╛рдКрдВрдЧрд╛ рдЬрд┐рд╕рдореЗрдВ рдореИрдВрдиреЗ рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдбреАрдмрдЧ рдХрд┐рдпрд╛ред


рдкреГрд╖реНрдарднреВрдорд┐ рдФрд░ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рдмрдпрд╛рдиред


рдПрдХ рдХреГрддреНрд░рд┐рдо рдЪреЗрд╕рд┐рд╕ рдореВрд▓ рд░реВрдк рд╕реЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдерд╛, рдФрд░ рдлрд┐рд░ рдЙрд╕ рдкрд░ рдПрдХ рдЦреБрджрд╛рдИ рдЗрдХрд╛рдИред рдЗрд╕ рд╡рд┐рдиреНрдпрд╛рд╕ рдиреЗ рдмрдбрд╝реА рдЕрдирд┐рдпрдорд┐рддрддрд╛рдУрдВ рдкрд░ рд╕рддрд╣ рдХреЗ рд╕рд╛рде рд╕рднреА рдкрд╣рд┐рдпреЛрдВ рдХрд╛ рд╕рдВрдкрд░реНрдХ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд┐рдпрд╛, рдЬрд┐рд╕рдореЗрдВ рдШреБрдорд╛ рднреА рд╢рд╛рдорд┐рд▓ рдерд╛ред

рдЫрд╡рд┐

рдЙрд╕ рддрд░рд╣

рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдЗрд╕реЗ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рд╕рдЯреАрдХ рд░реВрдк рд╕реЗ рд░рдЦрдиреЗ рдХреА рдЕрд╕рдВрднрд╡рддрд╛ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рдирд╛ рдкрдбрд╝рд╛, рдХреНрдпреЛрдВрдХрд┐ рдЕрдХреНрд╕рд░ рдкрд╣рд┐рдпреЗ рдиреАрдЪреЗ рдлрд┐рд╕рд▓ рдЬрд╛рддреЗ рдереЗ (рднреМрддрд┐рдХреА рд╕рдорд╕реНрдпрд╛ - рдЕрдзрд┐рдХрд╛рдВрд╢ рдмреНрд▓реЙрдХ (рдкрд╣рд┐рдпреЛрдВ рд╕рд╣рд┐рдд) рдореЗрдВ рдШрд░реНрд╖рдг рдХрд╛ рдЧреБрдгрд╛рдВрдХ рднреА рдмрд╣реБрдд рдХрдо рд╣реЛрддрд╛ рд╣реИ)ред рдСрд▓-рд╡реНрд╣реАрд▓-рдорд╛рдЙрдВрдЯреЗрдб рд╡реНрд╣реАрд▓ рдореЙрдбреНрдпреВрд▓ рдХреЗ рд╕рд╛рде рдкрд╣рд┐рдпрд╛ рдкреНрд▓реЗрдЯрдлреЙрд░реНрдо рдмрд╣реБрдд рдмреЛрдЭрд┐рд▓ рдерд╛ рдФрд░ рдЖрд╡рдзрд┐рдХ рднреМрддрд┐рдХреА рд╡рд┐рд╕реНрдлреЛрдЯ рд╕реЗ рдкреАрдбрд╝рд┐рдд рдерд╛ред рдирддреАрдЬрддрди, рдЪрд▓рдиреЗ рд╡рд╛рд▓реЗ рд░реЛрдмреЛрдЯ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ рдЧрдпрд╛ - рдЕрд░реНрдерд╛рддреН, рдПрдХ рд╣реЗрдХреНрд╕рд╛рдкреЙрдб, рд╕рдмрд╕реЗ рд╕реНрдерд┐рд░ рдЪрд▓рдиреЗ рд╡рд╛рд▓реЗ рдордВрдЪ рдХреЗ рд░реВрдк рдореЗрдВред

рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рд╡реНрдпрдХреНрддрд┐ рд╣реЗрдХреНрд╕рд╛рдкреЛрдбреНрд╕ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд╣рд╛рдБ рд╕реЗ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИ? рд╡рд╣ рд╢рд╛рдпрдж рдЦреЗрд▓ рдореЗрдВ рдЬрд╛рдПрдЧрд╛ рдФрд░ рдЕрдВрдЧреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рд░реЛрдмреЛрдЯ рд╢рд░реАрд░ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рд╢реБрд░реВ рдХрд░ рджреЗрдЧрд╛, рдФрд░ рдлрд┐рд░ рдпрд╣ рд╕реЛрдЪреЗрдВ рдХрд┐ рдпрд╣ рд╕рдм рдХреИрд╕реЗ рдкреБрдирд░реНрдЬреАрд╡рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПред рд▓реЗрдХрд┐рди рдпрд╣ рд╣рдорд╛рд░реА рд╡рд┐рдзрд┐ рдирд╣реАрдВ рд╣реИ (рдЧ)

рдореИрдВрдиреЗ рд╕рд┐рджреНрдзрд╛рдВрдд рд╕реЗ рд╢реБрд░реБрдЖрдд рдХреА


рдкреИрд░реЛрдВ рдХреА рд╕рдВрд░рдЪрдирд╛ рдХреЗ рд▓рд┐рдП, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдпреЛрдЬрдирд╛ рдХреЛ рдЪреБрдирд╛ рдЧрдпрд╛ рдерд╛:

рдЖрдВрддрд░рд┐рдХ рдЬреЛрдбрд╝ - рднреАрддрд░реА рдЬреЛрдбрд╝ рдЬреЛ рдХрд┐ рдзреБрд░реА (yaw) рд╕реЗ рдЧреБрдЬрд░рддрд╛ рд╣реИ
рдордзреНрдп рд╕рдВрдпреБрдХреНрдд рдФрд░ рдмрд╛рд╣рд░реА рд╕рдВрдпреБрдХреНрдд - рдмрд╛рд╣рд░реА рдЬреЛрдбрд╝ рдЬреЛ рдкрд┐рдЪ (рдкрд┐рдЪ) рдХреА рдзреБрд░реА рдХреЗ рд╕рд╛рде рд╕реНрд╡рд┐рдВрдЧ рдХрд░рддреЗ рд╣реИрдВред рд╕рдВрджрд░реНрдн рджрд┐рд╢рд╛ рдкреИрд░ рдХреЗ рдЖрдзрд╛рд░ рд╕реЗ рдкреИрд░ рдХреЗ рдЕрдВрдд рддрдХ рд╣реИред



рд╕рднреА рдЬреЛрдбрд╝реЛрдВ рдХреЗ рд▓рд┐рдП 0 рдХреЗ рдХреЛрдг рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдкреИрд░ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдмрдврд╝рд╛ рд╣реБрдЖ рд╣реИ (рдЦреЗрд▓ рдореЗрдВ рдПрдХ рд╕реАрдзрд╛ рдкреИрд░ рдмрдирд╛рдирд╛ рдЖрд╕рд╛рди рд╣реЛрдЧрд╛)ред

рдХрд╛рд░реНрдп рдПрдХ рджрд┐рдП рдЧрдП рд▓рдХреНрд╖реНрдп рдмрд┐рдВрджреБ рдХреЗ рд▓рд┐рдП рдЬреЛрдбрд╝реЛрдВ рдХреЗ рд░реЛрдЯреЗрд╢рди рдХреЗ рдРрд╕реЗ рдХреЛрдгреЛрдВ рдХреЛ рдЦреЛрдЬрдирд╛ рд╣реИ, рддрд╛рдХрд┐ рдкреИрд░ рдХрд╛ рдЕрдВрдд рдХрд┐рд╕реА рджрд┐рдП рдЧрдП рдмрд┐рдВрджреБ рдкрд░ рд╣реЛред рддреНрд░рд┐рдХреЛрдгрдорд┐рддрд┐ рдХреЛ рдпрд╛рдж рдХрд░рдиреЗ рдХрд╛ рд╕рдордпред

рдЖрдВрддрд░рд┐рдХ рд╕рдВрдпреБрдХреНрдд рдХреЗ рдХреЛрдг рдХреЛ рд▓рдХреНрд╖реНрдп рдХреЗ рдХреНрд╖реИрддрд┐рдЬ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЗ рдЖрд░реНрдХрдЯреЗрдиреНрдЬреЗрдВрдЯ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

const yawRad = Math.atan2(esimatedLegPosition.x, esimatedLegPosition.y); 

рджреЛ рдЕрдиреНрдп рдЬреЛрдбрд╝реЛрдВ рдХреЗ рд╕рд╛рде рдФрд░ рдЕрдзрд┐рдХ рдХрдард┐рди рд╣реИред рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╕рднреА рдЬреЛрдбрд╝реЛрдВ рдХреА рд▓рдВрдмрд╛рдИ рд╣реИред рдЖрдк рдХреЛрдг рдХреЛ рдХреНрд╖рд┐рддрд┐рдЬ рдФрд░ рдордзреНрдп рд╕рдВрдпреБрдХреНрдд рдФрд░ рдЬрдореАрди рдХреЗ рдмреАрдЪ рдХреА рджреВрд░реА, рд╕рд╛рде рд╣реА рд▓рдХреНрд╖реНрдп рдмрд┐рдВрджреБ рд╕реЗ рджреВрд░реА рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВред

рдЕрдЧрд▓рд╛, рдХреЛрд╕рд╛рдЗрди рдкреНрд░рдореЗрдп рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ, рдЖрдкрдХреЛ рдЬреНрдЮрд╛рдд рдкрдХреНрд╖реЛрдВ рдкрд░ рддреНрд░рд┐рдХреЛрдг рдХреЗ рдХреЛрдгреЛрдВ рдХреЛ рдЦреЛрдЬрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдЫрд╡рд┐

рдЫрд╡рд┐


тЖТ рддреНрд░рд┐рднреБрдЬ рд╡рд┐рд▓рдпрди

рддреЛ рдпрд╣ рдХреЛрдб рдореЗрдВ рджрд┐рдЦрддрд╛ рд╣реИ:

 getLegAngles(esimatedLegPosition) { const yawRad = Math.atan2(esimatedLegPosition.x, esimatedLegPosition.y); const dx = Math.hypot(esimatedLegPosition.x, esimatedLegPosition.y) - this.innerJoint.length; const dz = this.step.idlePosition.z + esimatedLegPosition.z; const hyp = Math.hypot(dx, dz); if (hyp > this.midJoint.length + this.outerJoint.length) {//out of reach hyp = this.midJoint.length + this.outerJoint.length; } const innerAngleRad = Math.acos((this.outerJoint.length * this.outerJoint.length - this.midJoint.length * this.midJoint.length - hyp * hyp) / (-2 * this.midJoint.length * hyp)) + Math.atan2(dz, dx); const outerAngleRad = Math.acos((hyp * hyp - this.midJoint.length * this.midJoint.length - this.outerJoint.length * this.outerJoint.length) / (-2 * this.midJoint.length * this.outerJoint.length)) - Math.PI; return { yaw: yawRad, midPitch: innerAngleRad, outerPitch: outerAngleRad }; } 

рдкреНрд░рд╕реНрддрд╛рд╡


рдЕрдЧрд▓рд╛ред рд░реЛрдмреЛрдЯ рдХреЛ рдЪрд▓рдирд╛ рд╣реИ, рд╣реИ рдирд╛? рдпрд╣реА рд╣реИ, рд╣рдореЗрдВ рдХрд┐рд╕реА рджрд┐рдП рдЧрдП рдкрдж рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЗ рдкреНрд░рддреНрдпреЗрдХ рдЪрд░рдг рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐ рдмрд╛рд░ N рдХреЛ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рджреЗрдЦрддреЗ рд╣реБрдП рдХрд┐ рдЙрдирдореЗрдВ рд╕реЗ 6 рдФрд░ 3 рдХреЗ рдкреИрд░ рдПрдВрдЯреАрдкреЗрдЬрд╝ рдореЗрдВ рдЪрд▓реЗ рдЬрд╛рддреЗ рд╣реИрдВ, рдпрд╣ рдХрд┐рд╕реА рддрд░рд╣ рдореБрд╢реНрдХрд┐рд▓ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред рд╣рдореЗрдВ рдЕрдореВрд░реНрддрддрд╛ рдХрд╛ рдПрдХ рдирдпрд╛ рд╕реНрддрд░ рд▓рд╛рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИред

рд▓реЗрдХрд┐рди рдХреНрдпрд╛ рд╣реЛрдЧрд╛ рдЕрдЧрд░ рд╣рдо рдХрд▓реНрдкрдирд╛ рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдкреИрд░ рдПрдХ рд╕рд░реНрдХрд▓ рдореЗрдВ рдЪрд▓рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдЗрд╕ рд╕рд░реНрдХрд▓ рдкрд░ рд╕реНрдерд┐рддрд┐ рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдХреЛрдг рдХреЛ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ? рдкрдХреНрд╖ рдХреЛ рд╣рдЯрд╛рдиреЗ рд╕реЗ рд╕реНрдерд╛рдпреА рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдХреЗрд╡рд▓ рдПрдХ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЪрдХреНрд░реАрдп рд░реВрдк рд╕реЗ рдмрджрд▓ рд░рд╣рд╛ рд╣реИред рддрдм рд▓рдХреНрд╖реНрдп рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╕рд╛рдЗрди рдФрд░ рдХреЛрд╕рд╛рдЗрди рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкрд╛рдП рдЬрд╛рддреЗ рд╣реИрдВред


рдЕрднреА рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд

рдпрд╣ рд╕реЛрдЪрдХрд░ рдХрд┐ рд╕рдм рдХреБрдЫ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░реЗрдЧрд╛, рдореИрдВрдиреЗ рдорд╣рд╕реВрд╕ рдХрд┐рдпрд╛ рдХрд┐ рдкрд╣рд▓реА рдмрд╛рд░ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣ рдХрд╛рд░реНрдп рдмрд╣реБрдд рдЬрдЯрд┐рд▓ рд╣реИ (рд╕реНрдкреЗрд╕ рдЗрдВрдЬреАрдирд┐рдпрд░реНрд╕ рдореЗрдВ рдбрд┐рдмрдЧрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рд╕рдм рдХреБрдЫ рдЦрд░рд╛рдм рд╣реИ, рд▓реЗрдХрд┐рди рдЕрдЧрд▓реЗ рднрд╛рдЧ рдореЗрдВ рдЙрд╕ рдкрд░ рдФрд░ рдЕрдзрд┐рдХ)ред

рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рдПрдХ рд╡рд┐рдЬрд╝реБрдЕрд▓рд╛рдЗрдЬрд╝рд░ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред рдореИрдВ рдЗрд╕реЗ рдЕрддрд┐рд░рд┐рдХреНрдд рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЗ рдмрд┐рдирд╛ рдмрдирд╛рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ рдФрд░ рдЗрд╕реЗ рдПрдХ рдХреНрд▓рд┐рдХ рдореЗрдВ рдФрд░ рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЗ рд╕рдВрджрд░реНрдн рдХреЗ рдмрд┐рдирд╛ рдЪрд▓рд╛рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдерд╛ред
рдЗрд╕рд▓рд┐рдП, JS + HTML рдХреИрдирд╡рд╛рд╕ рдХреЛ рдЪреБрдирд╛ рдЧрдпрд╛ред

рдЕрдм рдПрдХ рдЙрд▓реНрд▓реВ рдЦреАрдВрдЪрддреЗ рд╣реИрдВред

рдХреЛрдб:

рд╕реНрдкрд╛рдпрд▓рд░ рд╣реЗрдбрд┐рдВрдЧ
рд╡реЗрдХреНрдЯрд░:

 class Vector { constructor(x, y, z) { this.x = x; this.y = y; this.z = z; }; distanceTo(vector) { return Math.sqrt(Math.pow(this.x - vector.x, 2) + Math.pow(this.y - vector.y, 2) + Math.pow(this.z - vector.z, 2)); } diff(vector) { return new Vector( this.x - vector.x, this.y - vector.y, this.z - vector.z ); } add(vector) { return new Vector( this.x + vector.x, this.y + vector.y, this.z + vector.z ); } } 

рд╕рдВрдпреБрдХреНрдд:

 class Joint { constructor(angle, position, length) { this.angle = angle; this.position = position; this.length = length; this.targetAngle = angle; this.previousAngle = angle; this.velocity = 0; }; setTargetAngle(targetAngle) { this.targetAngle = targetAngle; this.velocity = this.targetAngle - this.normalizeAngle(this.angle); } normalizeAngle(angle) { while (angle <= -Math.PI) angle += Math.PI * 2; while (angle > Math.PI) angle -= Math.PI * 2; return angle; } getCurrentVelocity() {//per tick return this.normalizeAngle(this.angle - this.previousAngle); } tick() { this.previousAngle = this.angle; this.angle = this.angle + this.velocity; } } 

рдЪрд░рдг - рдкреИрд░ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рд╕рдВрд░рдЪрдирд╛:

 class Step { constructor( idlePosition,//vector relative to inner joint angle,//step direction length,//step length height,//step height phaseShift// ) { this.idlePosition = idlePosition; this.angle = angle;//radians this.length = length; this.height = height; this.phaseShift = phaseShift; } } 

рдкреИрд░:

 class Leg { constructor( vehicleCenter, innerJoint, midJoint, outerJoint, step, phaseStep ) { this.vehicleCenter = vehicleCenter; this.innerJoint = innerJoint; this.midJoint = midJoint; this.outerJoint = outerJoint; this.step = step; this.phaseStep = phaseStep; this.innerJoint.length = innerJoint.position.distanceTo(midJoint.position);//calculate this.midJoint.length = midJoint.position.distanceTo(outerJoint.position);//calculate //this.outerJoint.length = 100; this.joints = [innerJoint, midJoint, outerJoint]; this.preCalculateAngles(); } preCalculateAngles() { this.angles = {}; for (let phase = 0; phase < 360; phase += this.phaseStep) { this.angles[phase] = this.getLegAngles(this.getEsimatedLegPosition(phase, this.step.phaseShift)) } } applyStepHeight(z) { const idleYawRad = Math.atan2(this.step.idlePosition.x, this.step.idlePosition.y); const diffHypot = Math.hypot(this.step.idlePosition.x, this.step.idlePosition.y); const minZ = Math.abs(this.midJoint.length - this.outerJoint.length); const maxZ = (this.midJoint.length + this.outerJoint.length) * 0.6; if (Math.hypot(z, 0) > maxZ) { z = z > 0 ? maxZ : -maxZ; } const safeY = (this.innerJoint.length + this.midJoint.length * 0.5 + this.outerJoint.length * 0.5) * Math.cos(idleYawRad); const vAngle = Math.asin(z / safeY); const y = safeY * Math.cos(vAngle) * Math.cos(idleYawRad); this.step.idlePosition.z = z; this.step.idlePosition.y = this.step.idlePosition.y > 0 ? y : -y; this.preCalculateAngles(); } applyStepAngle(angle) { this.step.angle = angle; this.preCalculateAngles(); } applyPhase(phase/*0-360*/) { const legAngles = this.angles[phase]; this.innerJoint.setTargetAngle(legAngles.yaw); this.midJoint.setTargetAngle(legAngles.midPitch); this.outerJoint.setTargetAngle(legAngles.outerPitch); } getEsimatedLegPosition(phase, phaseShift) { phase = (phase + phaseShift) % 360; const stepX = ((phase < 180 ? phase : 180 - phase % 180) / 180 - 0.5) * this.step.length;//linear movement along step direction const stepZ = Math.max(Math.sin(phase * Math.PI / 180), -0.2) * this.step.height / 1.2; //const stepZ = Math.max((phase > 180 ? Math.cos(phase * Math.PI / 360) + 0.9 : Math.cos((phase - 120) * Math.PI / 360)) * .9 - .1, 0) * this.step.height; const x = this.step.idlePosition.x + stepX * Math.cos(this.step.angle); const y = this.step.idlePosition.y + stepX * Math.sin(this.step.angle); return new Vector(x, y, stepZ); } getLegAngles(esimatedLegPosition) { const yawRad = Math.atan2(esimatedLegPosition.x, esimatedLegPosition.y); const dx = Math.hypot(esimatedLegPosition.x, esimatedLegPosition.y) - this.innerJoint.length; const dz = this.step.idlePosition.z + esimatedLegPosition.z; const hyp = Math.hypot(dx, dz); if (hyp > this.midJoint.length + this.outerJoint.length) {//out of reach hyp = this.midJoint.length + this.outerJoint.length; } const innerAngleRad = Math.acos((this.outerJoint.length * this.outerJoint.length - this.midJoint.length * this.midJoint.length - hyp * hyp) / (-2 * this.midJoint.length * hyp)) + Math.atan2(dz, dx); const outerAngleRad = Math.acos((hyp * hyp - this.midJoint.length * this.midJoint.length - this.outerJoint.length * this.outerJoint.length) / (-2 * this.midJoint.length * this.outerJoint.length)) - Math.PI; if (isNaN(yawRad) || isNaN(innerAngleRad) || isNaN(outerAngleRad)) { console.log(yawRad, innerAngleRad, outerAngleRad); console.log(dx, dz); return; } return { yaw: yawRad, midPitch: innerAngleRad, outerPitch: outerAngleRad }; } getMaxMinAngles() { const angles = [0, 90, 180, 270].map((phase) => { return this.getLegAngles(getEsimatedLegPosition(phase, 0)); }); return { yawMin: Math.min(angles.map((x) => { return x.yaw })), yawMax: Math.max(angles.map((x) => { return x.yaw })), midPitchMin: Math.min(angles.map((x) => { return x.midPitch })), midPitchMax: Math.max(angles.map((x) => { return x.midPitch })), outerPitchMin: Math.min(angles.map((x) => { return x.outerPitch })), outerPitchMax: Math.max(angles.map((x) => { return x.outerPitch })), } } tick() { this.joints.forEach(function (joint) { joint.tick(); }); } getVectors() { const res = []; const sinYaw = Math.sin(this.innerJoint.angle); const cosYaw = Math.cos(this.innerJoint.angle); let currentVector = this.vehicleCenter; res.push(currentVector); currentVector = currentVector.add(this.innerJoint.position); res.push(currentVector); currentVector = currentVector.add(new Vector( this.innerJoint.length * sinYaw, this.innerJoint.length * cosYaw, 0 )); res.push(currentVector); const dxMid = Math.cos(this.midJoint.angle) * this.midJoint.length; const dzMid = Math.sin(this.midJoint.angle) * this.midJoint.length; currentVector = currentVector.add(new Vector( dxMid * sinYaw, dxMid * cosYaw, dzMid )); res.push(currentVector); const c = this.midJoint.angle + this.outerJoint.angle; const dxOuter = Math.cos(c) * this.outerJoint.length; const dzOuter = Math.sin(c) * this.outerJoint.length; currentVector = currentVector.add(new Vector( dxOuter * sinYaw, dxOuter * cosYaw, dzOuter )); res.push(currentVector); return res; } } 

рд░реЛрдмреЛрдЯ:

 class Hexapod { constructor(phaseStep) { this.idleHeight = -70; this.stepAngle = 0; this.turnAngle = 0; this.stepLength = 70; this.stepHeight = 30; this.debugPoints = []; const vehicleCenter = new Vector(0, 0, 0); this.legs = [ new Leg( vehicleCenter, new Joint(0, new Vector(-70, 10, 0), 50), new Joint(0, new Vector(-70, 60, 0), 50), new Joint(0, new Vector(-70, 110, 0), 70), new Step(new Vector(-30, 90, this.idleHeight), this.stepAngle, this.stepLength, this.stepHeight, 0), phaseStep ), new Leg( vehicleCenter, new Joint(0, new Vector(-70, -10, 0), 50), new Joint(0, new Vector(-70, -60, 0), 50), new Joint(0, new Vector(-70, -110, 0), 70), new Step(new Vector(-30, -90, this.idleHeight), this.stepAngle, this.stepLength, this.stepHeight, 180), phaseStep ), new Leg( vehicleCenter, new Joint(0, new Vector(0, 10, 0), 50), new Joint(0, new Vector(0, 60, 0), 50), new Joint(0, new Vector(0, 110, 0), 70), new Step(new Vector(0, 100, this.idleHeight), this.stepAngle, this.stepLength, this.stepHeight, 180), phaseStep ), new Leg( vehicleCenter, new Joint(0, new Vector(0, -10, 0), 50), new Joint(0, new Vector(0, -60, 0), 50), new Joint(0, new Vector(0, -110, 0), 70), new Step(new Vector(0, -100, this.idleHeight), this.stepAngle, this.stepLength, this.stepHeight, 0), phaseStep ), new Leg( vehicleCenter, new Joint(0, new Vector(70, 10, 0), 50), new Joint(0, new Vector(70, 60, 0), 50), new Joint(0, new Vector(70, 110, 0), 70), new Step(new Vector(30, 90, this.idleHeight), this.stepAngle, this.stepLength, this.stepHeight, 0), phaseStep ), new Leg( vehicleCenter, new Joint(0, new Vector(70, -10, 0), 50), new Joint(0, new Vector(70, -60, 0), 50), new Joint(0, new Vector(70, -110, 0), 70), new Step(new Vector(30, -90, this.idleHeight), this.stepAngle, this.stepLength, this.stepHeight, 180), phaseStep ), ]; } applyPhase(phase/*0-360*/) { this.legs.forEach(function (leg) { leg.applyPhase(phase); }); } changeHeight(value) { this.legs.forEach(function (leg) { leg.applyStepHeight(this.idleHeight + value); }, this); } changeStepLength(value) { this.stepLength += value; this.legs.forEach(function (leg) { leg.step.length = this.stepLength; leg.preCalculateAngles(); }, this); } applyTurn1(centerX, centerY) { const angleToAxis = Math.atan2(centerX, centerY); const distanceToAxis = Math.hypot(centerX, centerY); distanceToAxis = 1000/distanceToAxis; this.legs.forEach(leg => { const dx = leg.step.idlePosition.x + leg.innerJoint.position.x + Math.sin(angleToAxis)*distanceToAxis || 0; const dy = leg.step.idlePosition.y + leg.innerJoint.position.y + Math.cos(angleToAxis)*distanceToAxis || 0; const angle = Math.atan2(dy,dx); const hypIdle = Math.hypot(dx, dy); leg.applyStepAngle(angle+Math.PI/2); leg.step.length = this.stepLength *hypIdle/ ((distanceToAxis || 0) + 1000); }); } applyTurn(centerX, centerY) { this.stepAngle = Math.atan2(centerX, centerY); if (this.stepAngle > Math.PI / 2) this.stepAngle -= Math.PI; if (this.stepAngle < -Math.PI / 2) this.stepAngle += Math.PI; const mults = this.legs.map(leg => Math.hypot(leg.step.idlePosition.y + leg.innerJoint.position.y, leg.step.idlePosition.x + leg.innerJoint.position.x) / Math.hypot(leg.step.idlePosition.y + leg.innerJoint.position.y + centerY*.3, leg.step.idlePosition.x + leg.innerJoint.position.x + centerX*.3)); const minMult = Math.min(...mults); const maxMult = Math.max(...mults); const mult = minMult / maxMult; const d = Math.pow(Math.max(...this.legs.map(leg =>Math.hypot(leg.step.idlePosition.y + leg.innerJoint.position.y, leg.step.idlePosition.x + leg.innerJoint.position.x))),2)/Math.hypot(centerX,centerY); const a = Math.atan2(centerX,centerY); this.legs.forEach(leg => { const dx = leg.step.idlePosition.x + leg.innerJoint.position.x; const dy = leg.step.idlePosition.y + leg.innerJoint.position.y; const idleAngle = Math.atan2(dx, dy) + this.stepAngle; const turnAngle = Math.atan2(dx + centerX, dy + centerY); const hypIdle = Math.hypot(dx, dy); const hyp = Math.hypot(dx + centerX, dy + centerY); leg.applyStepAngle(turnAngle - idleAngle); leg.step.length = this.stepLength * hyp / hypIdle * mult; }); this.debugPoints = [new Vector(Math.sin(a)*-d,Math.cos(a)*-d,0)]; } tick() { this.legs.forEach(function (leg) { leg.tick(); }); } getVectors() { return this.legs.map(function (leg) { return leg.getVectors() }); } } 

рд▓реЗрдХрд┐рди рдбреНрд░рд╛рдЗрдВрдЧ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдХреБрдЫ рдФрд░ рдХрдХреНрд╖рд╛рдПрдВ рдЪрд╛рд╣рд┐рдП:

рдХреИрдирд╡рд╛рд╕ рдкрд░ рд▓рдкреЗрдЯреЗрдВ:

 class Canvas { constructor(id, label, axisSelectorX, axisSelectorY) { const self = this; this.id = id; this.label = label; this.canvas = document.getElementById(id); this.ctx = this.canvas.getContext('2d'); this.axisSelectorX = axisSelectorX; this.axisSelectorY = axisSelectorY; this.canvasHeight = this.canvas.offsetHeight; this.canvasWidth = this.canvas.offsetWidth; this.initialY = this.canvasHeight / 2; this.initialX = this.canvasWidth / 2; this.traceCounter = 0; this.maxTraces = 50; this.traces = {}; const axisSize = 150; this.axisVectors = [ [ new Vector(-axisSize, -axisSize, -axisSize), new Vector(-axisSize, -axisSize, axisSize) ], [ new Vector(-axisSize, -axisSize, -axisSize), new Vector(-axisSize, axisSize, -axisSize) ], [ new Vector(-axisSize, -axisSize, -axisSize), new Vector(axisSize, -axisSize, -axisSize) ], ] this.mouseOver = false; this.mousePos = { x: 0, y: 0 };//relative to center this.clickPos = { x: 0, y: 0 };//relative to center this.canvas.addEventListener("mouseenter", function (event) { self.mouseOver = true; }, false); this.canvas.addEventListener("mouseleave", function (event) { self.mouseOver = false; }, false); this.canvas.addEventListener("mousemove", function (event) { if (self.mouseOver) { self.mousePos = { x: event.offsetX - self.initialX, y: event.offsetY - self.initialY }; } }, false); this.canvas.addEventListener("mouseup", function (event) { if (self.mouseOver) { self.clickPos = { x: event.offsetX - self.initialX, y: event.offsetY - self.initialY }; } }, false); }; clear(drawAxis) { this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); this.ctx.strokeStyle = "#000000"; this.ctx.strokeText(this.label, 10, 10); if (drawAxis) { this.axisVectors.forEach(function (vectors, i) { this.ctx.moveTo(this.initialX, this.initialY); this.ctx.beginPath(); vectors.forEach(function (vector) { this.ctx.lineTo(this.initialX + this.axisSelectorX(vector), this.initialY - this.axisSelectorY(vector)); }, this); this.ctx.stroke(); const lastVector = vectors[vectors.length - 1]; this.traces[[this.traceCounter, i]] = lastVector }, this); } } drawVectors(vectors) {/*2d array*/ vectors.forEach(function (vectors, i) { this.ctx.moveTo(this.initialX, this.initialY); this.ctx.beginPath(); vectors.forEach(function (vector) { this.ctx.lineTo(this.initialX + this.axisSelectorX(vector), this.initialY - this.axisSelectorY(vector)); }, this); this.ctx.stroke(); const lastVector = vectors[vectors.length - 1]; this.traces[[this.traceCounter, i]] = lastVector }, this); for (const key in this.traces) { const vector = this.traces[key]; this.ctx.fillStyle = "#FF0000";//red this.ctx.fillRect(this.initialX + this.axisSelectorX(vector), this.initialY - this.axisSelectorY(vector), 1, 1); } this.ctx.strokeStyle = "#000000"; this.ctx.beginPath(); this.ctx.arc(this.clickPos.x + this.initialX, this.clickPos.y + this.initialY, 5, 0, 2 * Math.PI); this.ctx.stroke(); if (this.mouseOver) { this.ctx.strokeStyle = "#00FF00"; this.ctx.beginPath(); this.ctx.arc(this.mousePos.x + this.initialX, this.mousePos.y + this.initialY, 10, 0, 2 * Math.PI); this.ctx.stroke(); } this.traceCounter = (this.traceCounter + 1) % this.maxTraces; } drawPoints(points) { this.ctx.fillStyle = "#00ff00";//green points.forEach(function (point) { this.ctx.fillRect(this.initialX + this.axisSelectorX(point), this.initialY - this.axisSelectorY(point), 3, 3); }, this); } } 

рдЬреЛрдбрд╝реЛрдВ рдХреЗ рд╡рд░реНрддрдорд╛рди рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреИрд░ рд╡рд░реНрдЧ рдореЗрдВ рдПрдХ рд╡рд┐рдзрд┐ рд╣реИред рдпреЗ рд╡реЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдо рдЖрдХрд░реНрд╖рд┐рдд рдХрд░реЗрдВрдЧреЗред

рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рдЙрди рдмрд┐рдВрджреБрдУрдВ рдХреА рдПрдХ рдбреНрд░рд╛рдЗрдВрдЧ рднреА рдЬреЛрдбрд╝ рджреА рдЬрд╣рд╛рдВ рдкреИрд░ рдПрди рдЕрдВрддрд┐рдо рдЯрд┐рдХ рдореЗрдВ рдерд╛ред

рдФрд░ рдЕрдВрдд рдореЗрдВ, рдПрдХ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдЬреЛ рд╕рд┐рдореБрд▓реЗрд╢рди рдЪрд▓рд╛рдПрдЧрд╛:

 class Worker { constructor(tickTime) { const self = this; this.phaseStep = 5; this.tickTime = tickTime; const tan30 = Math.tan(Math.PI / 6); const scale = 0.7; this.canvases = [ new Canvas('canvasForward', 'yz Forward', function (v) { return vy }, function (v) { return vz }), new Canvas('canvasSide', 'xz Side', function (v) { return vx }, function (v) { return vz }), new Canvas('canvasTop', 'xy Top', function (v) { return vx }, function (v) { return -vy }), new Canvas('canvasIso', 'xyz Iso', function (v) { return vx * scale + vy * scale }, function (v) { return vz * scale + vx * tan30 * scale - vy * tan30 * scale }), ]; this.bot = new Hexapod(this.phaseStep); this.phase = 0; this.focus = true; window.addEventListener('focus', function () { console.log('focus'); self.focus = true; }); window.addEventListener('blur', function () { console.log('blur'); self.focus = false; }); this.start(); } tick(argument) { const canvasForward = this.canvases[0]; const bot = this.bot; if (canvasForward.mouseOver) { bot.changeHeight(-canvasForward.mousePos.y); } else { bot.changeHeight(0); } const canvasTop = this.canvases[2]; if (canvasTop.mouseOver) { bot.applyTurn(-canvasTop.mousePos.x, -canvasTop.mousePos.y); } else { bot.applyTurn(0, 0); } this.phase = (this.phase + this.phaseStep) % 360; bot.applyPhase(this.phase); bot.tick(); const vectors = bot.getVectors(); this.canvases.forEach(function (c) { c.clear(false); c.drawVectors(vectors); c.drawPoints(bot.debugPoints); }); } start() { this.stop(); this.interval = setInterval((function (self) { return function () { if (self.focus) { self.tick(); } } })(this), this.tickTime); } stop() { clearInterval(this.interval); } } 


рдкрд░рд┐рдгрд╛рдо:


рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрдЪреНрдЫрд╛ рд╣реИ?

рдпрд╣рд╛рдВ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдкреИрд░реЛрдВ рдХрд╛ рдорд╛рд░реНрдЧ рд╕рд░реНрдХрд▓ рд╕реЗ рдЕрд▓рдЧ рд╣реИред рдКрд░реНрдзреНрд╡рд╛рдзрд░ рдЖрдВрджреЛрд▓рди рдПрдХ рдЫрдВрдЯрдиреА рд╕рд╛рдЗрди рд▓рд╣рд░ рдЬреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ, рдФрд░ рдХреНрд╖реИрддрд┐рдЬ рдЖрдВрджреЛрд▓рди рд░реИрдЦрд┐рдХ рд╣реИред рдЗрд╕рд╕реЗ рдкреИрд░реЛрдВ рдкрд░ рднрд╛рд░ рдХрдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

рдЕрдм рдХреБрдЫ рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдХреЛрдб рдореЗрдВ рдХреНрдпрд╛ рд╣реЛ рд░рд╣рд╛ рд╣реИред

рд░реЛрдмреЛрдЯ рдХреЛ рдЪрд╛рд▓реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреИрд╕реЗ рд╕рд┐рдЦрд╛рдПрдВ?


рдореБрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ 2 рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдХреЛ рджреЗрдЦрд╛:

рдпрджрд┐ рд░реЛрдмреЛрдЯ рдЦрдбрд╝рд╛ рд╣реИ, рддреЛ рдкреИрд░ рдПрдХ рд╕рд░реНрдХрд▓ рдореЗрдВ рдЪрд▓рддреЗ рд╣реИрдВред

рдХреЗрд╡рд▓ рдПрдХ рдЪреАрдЬ рд╣реИ, рдкрд░рд┐рдзрд┐ рдХреЗ рдЪрд╛рд░реЛрдВ рдУрд░ рдХреА рдЧрддрд┐ рд╡рд░реНрддрдорд╛рди рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде рдХреЛрдб рдХреЛ рдмрд╣реБрдд рдЬрдЯрд┐рд▓ рдХрд░реЗрдЧреАред рдЗрд╕рд▓рд┐рдП, рдкреИрд░ рд╕рд░реНрдХрд▓ рдореЗрдВ рд╕реНрдкрд░реНрд╢рд░реЗрдЦрд╛ рд░реВрдк рд╕реЗ рдЪрд▓рддреЗ рд╣реИрдВред

рдЬрдм рд░реЛрдмреЛрдЯ рдЪрд▓рддрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рдПрдХ рдЕрдВрддрд░ рдХреЗ рд╕рд╛рде рдПрдХрд░рдореИрди рд╕реНрдЯреАрдпрд░рд┐рдВрдЧ рдЬреНрдпрд╛рдорд┐рддрд┐ рдЬреИрд╕рд╛ рдХреБрдЫ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рдЫрд╡рд┐

рдпрд╣реА рд╣реИ, рдПрдХ рдЫреЛрдЯреЗ рддреНрд░рд┐рдЬреНрдпрд╛ рдХреЗ рд╕рд╛рде рдЪрд▓рдиреЗ рд╡рд╛рд▓реЗ рдкреИрд░реЛрдВ рдХреА рдЪрд░рдг рд▓рдВрдмрд╛рдИ рдХрдо рд╣реИред рдФрд░ рд░реЛрдЯреЗрд╢рди рдХреЛрдг рдЕрдзрд┐рдХ рд╣реИред

рдкреНрд░рддреНрдпреЗрдХ рдкреИрд░ рдХреЗ рд▓рд┐рдП рд░реЛрдЯреЗрд╢рди рдХреЗ рдХреЛрдг рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЗ рд╕рд╛рде рдЖрдпрд╛:

1. рд╣рдо рдкреИрд░ рдХреА рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реНрдерд┐рддрд┐ рд╕реЗ рдХреЛрдг рдХреЛ рд░реЛрдмреЛрдЯ рдХреЗ рдХреЗрдВрджреНрд░ рдореЗрдВ рдорд╛рдирддреЗ рд╣реИрдВ:

 const idleAngle = Math.atan2(dx, dy) + this.stepAngle; 

2. рд╣рдо рдкреИрд░ рдХреА рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реНрдерд┐рддрд┐ рд╕реЗ рдХреЛрдг рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ (рд░реЛрдмреЛрдЯ рдХрд╛ рдХреЗрдВрджреНрд░ + рдЬреЛ рдШреБрдорд╛рд╡ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИ, рд╡рд╣ рдПрдХ рдЪрд░ рдкреИрд░рд╛рдореАрдЯрд░ рд╣реИ):

 const turnAngle = Math.atan2(dx + centerX, dy + centerY); 

3. рдЗрди рдХреЛрдгреЛрдВ рдХреЗ рдЕрдВрддрд░ рдХреА рдУрд░ рдХрджрдо рдмрдврд╝рд╛рдПрдБ:

 leg.applyStepAngle(turnAngle - idleAngle); 

рд▓реЗрдХрд┐рди рдпрд╣ рд╕рдм рдирд╣реАрдВ рд╣реИред рдЕрднреА рднреА рд╕реНрдЯреНрд░рд╛рдЗрдб рд▓рдВрдмрд╛рдИ рдмрджрд▓рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИред рдорд╛рдереЗ рдореЗрдВ рдПрд╣рд╕рд╛рд╕ - рдХреЗрдВрджреНрд░ рдХреА рджреВрд░реА рдХреЛ рдмрджрд▓рдХрд░ рдХрджрдо рдХреА рд▓рдВрдмрд╛рдИ рдХреЛ рдЧреБрдгрд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП - рдПрдХ рдШрд╛рддрдХ рджреЛрд╖ рдерд╛ - рдмрд╛рд╣рд░реА рдкреИрд░ рдмрд╣реБрдд рд╡реНрдпрд╛рдкрдХ рд░реВрдк рд╕реЗ рдЪрд▓реЗ рдЧрдП рдФрд░ рдПрдХ рджреВрд╕рд░реЗ рдХреЛ рдЪреЛрдЯ рдкрд╣реБрдВрдЪрд╛рдиреЗ рд▓рдЧреЗред

рдЗрд╕рд▓рд┐рдП, рдореБрдЭреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдЬрдЯрд┐рд▓ рдмрдирд╛рдирд╛ рдкрдбрд╝рд╛:

1. рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рдкреИрд░ рдХреЗ рд▓рд┐рдП рдХреЗрдВрджреНрд░ рдХреА рджреВрд░реА рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ:

 const mults = this.legs.map(leg => Math.hypot(leg.step.idlePosition.y + leg.innerJoint.position.y, leg.step.idlePosition.x + leg.innerJoint.position.x) / Math.hypot(leg.step.idlePosition.y + leg.innerJoint.position.y + centerY*.3, leg.step.idlePosition.x + leg.innerJoint.position.x + centerX*.3)); 

0.3 - рдЬрд╛рджреВ рдХреА рд╕рдВрдЦреНрдпрд╛

2. рдиреНрдпреВрдирддрдо рдФрд░ рдЕрдзрд┐рдХрддрдо рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рдмреАрдЪ рд╕рдВрдмрдВрдз рдЦреЛрдЬреЗрдВ

 const minMult = Math.min(...mults); const maxMult = Math.max(...mults); const mult = minMult / maxMult; 

рдпрд╣ рдХрд╛рд░рдХ рдХреЗрдВрджреНрд░ рдХреА рджреВрд░реА рдореЗрдВ рдиреНрдпреВрдирддрдо рдФрд░ рдЕрдзрд┐рдХрддрдо рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рдХреЛ рджрд░реНрд╢рд╛рддрд╛ рд╣реИред рдпрд╣ рд╣рдореЗрд╢рд╛ 1 рд╕реЗ рдХрдо рд╣реЛрддрд╛ рд╣реИ, рдФрд░ рдпрджрд┐ рдЖрдк рдЗрд╕рдХреЗ рджреНрд╡рд╛рд░рд╛ рд╕реНрдЯреНрд░рд╛рдЗрдб рд▓рдВрдмрд╛рдИ рдХреЛ рдЧреБрдгрд╛ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдЙрди рдкреИрд░реЛрдВ рдХреЗ рд▓рд┐рдП рднреА рдирд╣реАрдВ рдмрдврд╝реЗрдЧрд╛, рдЬреЛ рдШреБрдорд╛рд╡ рдХреА рджрд┐рд╢рд╛ рдХреЗ рд▓рд┐рдП рдмрд╛рд╣рд░реА рд╣реИрдВред

 const hypIdle = Math.hypot(dx, dy); const hyp = Math.hypot(dx + centerX, dy + centerY); leg.step.length = this.stepLength * hyp / hypIdle * mult; 

рдпрд╣рд╛рдВ рдмрддрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ (gif 2 рдореЗрдЧрд╛рдмрд╛рдЗрдЯ):

рдЬрд┐рдлрд╝ 2 рдореЗрдЧрд╛рдмрд╛рдЗрдЯ


тЖТ рдЖрдк рдпрд╣рд╛рдБ рдкрд░рд┐рдгрд╛рдо рдХреЗ рд╕рд╛рде рдЦреЗрд▓ рд╕рдХрддреЗ рд╣реИрдВ

рдХрд░реАрдм рд╕реЗ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВ рдПрдХ html рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╕рд╛рдордЧреНрд░реА рдХреЛ рд╕рд╣реЗрдЬрдиреЗ рдФрд░ рдЖрдкрдХреЗ рдкрд╕рдВрджреАрджрд╛ рдкрд╛рда рд╕рдВрдкрд╛рджрдХ рдореЗрдВ рдЬрд╛рд░реА рд░рдЦрдиреЗ рдХреА рд╕рд▓рд╛рд╣ рджреЗрддрд╛ рд╣реВрдВред

рдЕрдЧрд▓реА рдкреЛрд╕реНрдЯ рдореЗрдВ, рдореИрдВ рдЖрдкрдХреЛ рдмрддрд╛рдКрдВрдЧрд╛ рдХрд┐ рдХреИрд╕реЗ рдореИрдВрдиреЗ рдЗрд╕реЗ рд╕реНрдкреЗрд╕ рдЗрдВрдЬреАрдирд┐рдпрд░реНрд╕ рдХреЗ рд▓рд┐рдП рдХрд╛рдо рдХрд┐рдпрд╛ред
Spoiler: рдкреНрд░реЛрдЧреНрд░рд╛рдореЗрдмрд▓ рдмреНрд▓реЙрдХ рдореЗрдВ рдЖрдк C # рд▓рдЧрднрдЧ рдирд╡реАрдирддрдо рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВред

Source: https://habr.com/ru/post/hi426195/


All Articles