ODEプチキャンプ2:ロボカップ・プチシミュレータを作ろう!
ode
2006.10.19
時が経つのは本当に早いもので,この間暑いと思っていたらもうすっかり秋ですね.10月16~18日と妙高高原にある大学のセミナーハウスに研究室の学生と行き,まじめに輪講やテニスを楽しみました.山はもうすっかり紅葉が色づき美しかったのが印象的でした.
さて,Open Dynamics Engine プチキャンプもデビューしてから2ヶ月がすぎ,新しいロボコンマガジン(No.48)が10月14日(土)に発売されました.
今回は連載2回目となります.連載が全4回しかないのにも関わらず,グリーディ(greedy)にも最終回は2脚歩行ロボットのプチシミュレータを作ることを目標としているため,今回は車輪型ロボット,次回はロボットアームのプチシミュレータとドンドン作っていきます.
ロボコンマガジンでは紙面が限られているため詳しく説明できません.このページではそれを補うため,サンプルプログラムの全ソースコードと豪華付録?として連載に登場したAPI集を掲載しています.
プログラム1(自由落下:衝突検出なし)
ゆっくり落下していくと...
プログラム2(自由落下:衝突検出あり)
地面に着陸
プログラム3(プチシミュレータ)
ロングシュート!
注1:これらのプログラムはODE教育用のサンプルプログラムとして書いたもので,ロボコンマガジンの記事で説明している全サンプルプログラムです.上のプログラムを自由にダウンロードして遊んでください.これらのプログラムは無保証です.なお,再配布及び無断転載等はお止めください.
注2:mingw, linux, cygwin. OS Xに対応しています.ご自分の環境のmakefile(例えばmakefile.linux)をmakefileにコピーしてからmakeしてください.なお,オリジナルのmakefileはmingw用になっていますのでmingwを利用されている方はコピーする必要はありません.
makeに失敗する方へ
makeを実行したとき[cannot found -lode]などとコンピュータに怒られた方はodeのライブラリが所定の位置にインストールされていません。mingwやcygwinターミナルを開き以下のコマンドを実行してください。
cd /home/ユーザ名/src/ode-0.6
make install
また,連載を読んでよくわからない方は是非このページで質問してください.感想やご意見等も大歓迎です.
今回の目玉は上にあるロボカップ中型ロボットリーグのプチシミュレータです.画面の中央より少し左に見える赤い物体はボールで,中央にある黒い物体はロボットで,Fu Fightersのロングシュートをシミュレートしています.遊び方やダウンロードの方法はロボコンマガジンNo.48の記事を読んでください.
プログラム1
プログラム2
プログラム3
実行開始時の設定ではカメラを遠方に設定しているためロボット等がよく見えません.
ウインドウの隅をドラッグしてウインドウを大きくするか,マウスボタン(左,右)を押しなら画面をドラッグすると視線,大きさなどを変更できます.
makeに失敗する方へ
makeを実行したとき[-lode cannot found]などと怒られた方はodeのライブラリが所定の位置にインストールされていません。mingwやcygwinターミナルを開き以下のコマンドを実行してください。
cd /home/ユーザ名/src/ode-0.6
豪華付録?API集
dBodyID dBodyCreate(dWorldID world)
剛体を作り、そのID番号を戻り値として返す
const dReal *dBodyGetPosition(dBodyID body)
剛体bodyの重心位置を取得しそのポインタを返す
const dReal *dBodyGetRotation(dBodyID body)
回転行列を取得しそのポインタを返す
void dBodySetMass(dBodyID body, const dMass *mass)
剛体bodyに質量パラメータmassを設定する
void dBodySetPosition(dBodyID body, dReal x, dReal y, dReal z)
剛体bodyの重心を絶対座標系(x,y,z)の位置に設定する
void dBodySetRotation(dBodyID body, const dMatrix3 R)
剛体bodyを回転行列Rで表す姿勢に設定する
dGeomID dCreateBox(dSpaceID space, dReal lx, dReal ly, dReal lz)
spaceに各辺の長さ(lx,ly,lz)の直方体ジオメトリを生成する
dGeomID dCreateCylinder(dSpaceID space, dReal r, dReal l)
半径r、長さlの円柱ジオメトリを生成し、そのID番号を返す
dGeomID dCreatePlane(dSpaceID space ,dReal a, dReal b, dReal c, dReal d)
spaceにax+by+cz=dの平面ジオメトリを生成する
void dGeomSetBody(dGeomID geom, dBodyID body)
物体の2つの属性であるジオメトリgeomと剛体bodyを関連づける
dSpaceID dHashSpaceCreate(0)
衝突計算用スペースを生成し、そのID番号を返す
void dJointGroupEmpty(dJointGroupID)
接触点が格納されているジョイントグループを空にする
void dJointAttach(dJointID joint, dBodyID body1, dBodyID dBody2)
剛体body1とbody2にジョイントjointを取り付ける
dJointGroupID dJointGroupCreate(0)
接触点のグループを格納するジョイントグループを生成し、そのID番号を返す
void dJointSetHingeAnchor(dJointID joint, dReal x, dReal y, dReal z)
ヒンジジョイントの回転軸の中心点(x,y,z)を設定する
void dJointSetHingeAxis(dJointID joint, dReal x, dReal y, dReal z)
ヒンジジョイントの回転軸ベクトル(x,y,z)を設定する
void dMassSetBoxTotal(dMass *mass, dReal m, dReal lx, dReal ly, dReal lz)
サイズ(lx,ly,lz)、全質mの直方体の質量パラメータmassを計算する
void dMassSetCylinderTotal(dMass *mass, dReal m, int dir, dReal r, dReal l)
方向dir(1=x,2=y,3=z)、半径r、長さl、全質量mの円柱の質量パラメータmassを計算する
void dMassSetZero(dMass *mass)
質量パラメータmassの要素を0に初期化する
void dRFromAxisAndAngle(dMatrix3 R, dReal ax, dReal ay, dReal az, dReal angle)軸ベクトル(ax,ay,az)周りにangle[rad]回転させた時の回転行列Rを求める
void dSpaceCollide(dSpaceID space, void *data, dNearCallback *callback)
spaceで衝突する可能性のある2つのジオメトリを選び出し、引数のcallback関数を呼び出す。dataはcallback関数へ渡すデータへのポインタ
dWorldID dWorldCreate()
物理計算用ワールドを生成し、そのID(識別)番号を戻り値として返す
void dWorldDestroy(dWorldID world)
物理計算用ワールドworldを破壊する
void dWorldSetGravity(dWorldID world, dReal x, dReal y, dReal z)
ワールドworldに(x,y,z)[m/s2]の重力加速度を設定する
void dWorldStep(dWorldID world, dReal step_size)
ワールドworldのシミュレーションを1ステップ進める。2番目の引数step_sizeは積分のステップ時間[s]
void dsDrawBoxD(const double pos[3], const double R[12], const double sides[3])位置posに回転行列Rを取るサイズsidesの直方体を描画する
void dsSetViewPoint(float xyz[3], float hpr[3])
視点xyzと視線hpr[°]を設定する
void dsSimulationLoop(int argc, char **argv,int w, int h, dsfunction *fn)
シミュレーションループを呼び出す。1、2番目の引数はmain関数の引数で、3,4番目は描画ウインドウの大きさ(横w,縦h)で、5番目の引数は描画関数クラス
").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1
").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0
タイトルとURLをコピーしました
コメント
shanさん、
新年あけましておめでとうございます。今年もdemura.netをよろしくお願いします。
妙高高原にスキーにいって返事が遅くなりました。今年は雪が少ないです。
さて、simLoop関数はサンプルプログラムでは見えませんがwhileループの中にあり、シミュレーションの各ステップ毎によびだされています。
simLoop関数の中に各ステップ毎のロボットの動作を記述してください。
このサンプルを改造してロボットにあらかじめプログラムした動きをさせたいのですが、どこを変えればいいのでしょうか?
最終的にはstatic void simLoopのところにleft_velとright_velの値を書き出すようにすればいいかと思いますが、どこにループを作ってロボットの動きを計算させたらいいかわかりません。
よろしくお願いします。
片岡さん、
いつも貴重なコメントありがとうございます。大変助かります。
ソースコードの該当箇所を2から2.0に変更しました。
VCでのコンパイルエラーの報告です。
サンプルのプログラム3で282行目の部分ですが
dReal vmax45 = POWER * 0.01 * 8.0 / sqrt(2);
関数sqrt()の引数を2.0か2.0fに変えないとオーバーロードが解決出来ないというエラーが発生してしまいます。