2: 2017-09-03 (日) 22:52:04 takaboo |
現: 2017-09-04 (月) 18:01:25 takaboo |
| なお、APIが返すエラー情報はここでは無視しています。 | | なお、APIが返すエラー情報はここでは無視しています。 |
| | | |
- | ***サブルーチン [#bd74705b] | + | ***各種サブルーチン [#bd74705b] |
- | 必要最低限の動作フローを元しているため、本来行わなくてはならないエラー処理をかなり省略しています。特に返り値のみで判断せざるを得ないサブルーチンは、使用しているAPIでエラーが発生すると想定外の動作を励起する事が想定されます。~ | + | ここでは必要最低限の動作フローを元しているため、本来行わなくてはならないエラー処理をかなり省略しています。特に返り値のみで判断せざるを得ないサブルーチンは、使用しているAPIでエラーが発生すると想定外の動作を励起する事が想定されます。~ |
- | 信頼性の高いシステムを構築する場合は可能な限り厳密なエラー処理を追加し、処理が容易に進捗しないコードを心がけるべきです。 | + | 信頼性の高いシステムを構築する場合は可能な限り厳密なエラー処理を追加し、処理が容易に進捗しないコードを心がけるべきなので、あくまで参考として紹介します。 |
| + | |
| + | Dynamixel PROシリーズはモデルによってコントロールテーブル上の各アイテムの扱う値の範囲が異なっていたり、物理値を扱うものやDynamixelの内部でのみ意味のある値があるため、各アイテムを直接扱うとなると互換性のあるプログラムが作りにくい構成になっています。~ |
| + | ここでは主にモデル間の差異を吸収して角度として扱うためのサブルーチンと、動作するにあたって最低限必要なサブルーチンを紹介します。~ |
| + | なお、複数軸を同時に扱う場合はこの限りではありません。 |
| | | |
| ****トルクイネーブル [#wbcb12a2] | | ****トルクイネーブル [#wbcb12a2] |
| 出力軸を動かすには、予めTorque Enable(562)に1を書き込んでおく必要があります。また、制御を停止し出力軸をフリーにするにはTorque Enableに0を書き込みます。~ | | 出力軸を動かすには、予めTorque Enable(562)に1を書き込んでおく必要があります。また、制御を停止し出力軸をフリーにするにはTorque Enableに0を書き込みます。~ |
- | なお、Torque Enableが0の状態から1を書き込んだ直後、出力軸はPresent PositionをGoal Positionとして位置決め制御を開始しますので、Torque Enableの操作のみで出力軸が実際に回転し始めることはありません。 | + | なお、Torque Enableが0の状態から1を書き込んだ直後、内部でPresent PositionをGoal Positionにコピーしてから位置決め制御を開始しますので、Torque Enableの操作のみで出力軸が勝手に回転し始めることはありません。 |
| #html{{ | | #html{{ |
| <pre class="brush: c"> | | <pre class="brush: c"> |
| }} | | }} |
| | | |
- | ****角度と位置の変換 [#o0d33109] | + | ****角度と位置及び回転数の変換 [#o0d33109] |
- | モデル毎に扱うGoal Positionの分解能が異なり、角度を元に位置を指令するにしても変換が必要です。~ | + | モデル毎に扱うGoal Positionの分解能やGoal Velocityの単位が異なっているため、いずれも物理値を元に指令する場合は変換が必要です。~ |
- | Max Position Limit(36~39)やMin Position Limit(40~43)が初期状態であれば、それら利用して係数を算出しておき、モデル毎の差異を吸収して角度と位置を相互に変換できます。 | + | Goal PositionについてはMax Position Limit(36~39)やMin Position Limit(40~43)が初期状態であれば、それら利用して係数を算出しておき、モデル毎の差異を吸収して角度と位置を相互に変換できます。 |
| #html{{ | | #html{{ |
| <pre class="brush: c"> | | <pre class="brush: c"> |
| static double AngleToPosGain = 0; | | static double AngleToPosGain = 0; |
| // 位置上下限リミット値 | | // 位置上下限リミット値 |
- | static int32_t maxpos = 0, minpos = 0; | + | static int32_t maxvelo = 0, maxpos = 0, minpos = 0; |
| | | |
| // 位置上下限値取得 | | // 位置上下限値取得 |
| bool GetMaxMinLimit (TDeviceID dev, uint8_t id) { | | bool GetMaxMinLimit (TDeviceID dev, uint8_t id) { |
- | if ((maxpos != 0) && (minpos != 0)) return true; | + | if ((maxvelo != 0) && (maxpos != 0) && (minpos != 0)) return true; |
| else { | | else { |
| return | | return |
| + | DX2_ReadLongData (dev, id, 32, (uint32_t *)&maxvelo, NULL) && |
| DX2_ReadLongData (dev, id, 36, (uint32_t *)&maxpos, NULL) && | | DX2_ReadLongData (dev, id, 36, (uint32_t *)&maxpos, NULL) && |
| DX2_ReadLongData (dev, id, 40, (uint32_t *)&minpos, NULL); | | DX2_ReadLongData (dev, id, 40, (uint32_t *)&minpos, NULL); |
| | | |
| ****出力軸の角度を指令 [#ea03d19f] | | ****出力軸の角度を指令 [#ea03d19f] |
- | Torque Enableが1の状態であれば、Goal Position(596~599)へ書き込んだ値に応じて出力軸が移動します。Goal PositionはMax Position Limit~Min Position Limitの範囲を超えた値を受け取らないため、ここでは上下限値で飽和させた上で指令しています。 | + | Operating Mode(11)が1,3でかつTorque Enable(562)が1の状態であれば、Goal Velocity(600~603)へ書き込んだ値に応じて出力軸の回転速度が決まります。Goal Velocityの絶対値がVelocity Limit(32~35)を超えた値を受け取らないため、ここではその値で飽和させた上で指令しています。 |
| + | #html{{ |
| + | <pre class="brush: c"> |
| + | // 回転数指令 |
| + | bool SetGoalVelocity (TDeviceID dev, uint8_t id, double rpm) { |
| + | int32_t velo = max (min (RpmToVelo (dev, id, rpm), -maxvelo), maxvelo); |
| + | return DX2_WriteLongData (dev, id, 600, velo, NULL); |
| + | } |
| + | </pre> |
| + | }} |
| + | |
| + | ****出力軸の回転速度を指令 [#ea03d19f] |
| + | Operating Mode(11)が3でかつTorque Enable(562)が1の状態であれば、Goal Position(596~599)へ書き込んだ値に応じて出力軸が移動します。Goal PositionはMax Position Limit~Min Position Limitの範囲を超えた値を受け取らないため、ここでは上下限値で飽和させた上で指令しています。 |
| #html{{ | | #html{{ |
| <pre class="brush: c"> | | <pre class="brush: c"> |
| // 角度指令 | | // 角度指令 |
- | bool SetGoalAngle (TDeviceID dev, uint8_t id, double angle) { | + | bool SetGoalVelocity (TDeviceID dev, uint8_t id, double rpm) { |
- | int32_t pos = max (min (AngleToPos (dev, id, angle), minpos), maxpos); | + | int32_t velo = max (min (RpmToVelo (dev, id, angle), -maxvelo), maxvelo); |
- | return DX2_WriteLongData (dev, id, 611, pos, NULL); | + | return DX2_WriteLongData (dev, id, 600, pos, NULL); |
| + | } |
| + | </pre> |
| + | }} |
| + | |
| + | ****動作モードの変更 [#vfdea87f] |
| + | Operating Mode(11)のデフォルト値はPosition Control Modeですが、モードが違えば操作も異なりますし、思い通りの動作が行われない事も起こりえます。~ |
| + | なお、動作モードの変更はTorque Enable(562)が0の状態でのみ可能です。 |
| + | #html{{ |
| + | <pre class="brush: c"> |
| + | // モード設定 |
| + | bool SetOperatingMode (TDeviceID dev, uint8_t id, uint8_t mode) { |
| + | if ((mode >=0) && (mode <= 4)) { |
| + | if (DX2_WriteByteData (dev, id, 562, 0, NULL)) |
| + | return DX2_WriteByteData (dev, id, 11, mode, NULL); |
| + | } |
| + | return false; |
| } | | } |
| </pre> | | </pre> |
| | | |
| ***角度を指令 [#u8f17a6a] | | ***角度を指令 [#u8f17a6a] |
- | 紹介した各処理ルーチンを使用し、Goal Position(596~599)へ-180~180度の範囲で10度ずつ角度を増やして指令します。 | + | 紹介した各処理ルーチンを使用し、現在位置を起点に最大動作範囲を往復回転させるプログラムです。 |
| #html{{ | | #html{{ |
| <pre class="brush: c"> | | <pre class="brush: c"> |
| | | |
| ***IDの変更 [#f5276f71] | | ***IDの変更 [#f5276f71] |
- | IDの変更自体は該当のアドレスに新しいIDを書き込むだけですが、変更先のIDを持ったデバイスが無い事を確認した上で変更するプログラムを紹介します。 | + | IDの変更は該当のアドレスに新しいIDを書き込む事で行いますが、複数のデバイスが接続された環境での変更はIDの競合が発生する等のリスクがあります。~ |
| + | 手動で既存のIDを調べながら変更するのは少々手間なので、変更先のIDを持ったデバイスが無い事を確認して更新するプログラムを紹介します。 |
| #html{{ | | #html{{ |
| <pre class="brush: c"> | | <pre class="brush: c"> |
| #define _TORQUE_ENABLE 562 // 8bit | | #define _TORQUE_ENABLE 562 // 8bit |
| #define _ID 7 // 8bit | | #define _ID 7 // 8bit |
- | | |
- | bool SetTorqueEnable (TDeviceID dev, uint8_t id, bool en) { | |
- | return DX2_WriteByteData (dev, id, _TORQUE_ENABLE, en ? 1 : 0, NULL); | |
- | } | |
| | | |
| void main (void) { | | void main (void) { |
| TDeviceID dev; | | TDeviceID dev; |
| int id1, id2; | | int id1, id2; |
- | if ((dev = DX2_OpenPort ("\\\\.\\COM7", 57600))) { | + | printf ("input current ID number = "); |
- | printf ("input previous id = "); | + | scanf ("%d", &id1); |
- | scanf ("%d", &id1); | + | printf ("input updated ID number = "); |
- | printf ("input following id = "); | + | scanf ("%d", &id2); |
- | scanf ("%d", &id2); | + | if (id1 != id2) { |
- | if (id1 != id2) { | + | if ((dev = DX2_OpenPort ("\\\\.\\COM7", 57600))) { |
| + | printf ("Opened COM port.\n"); |
| // 元のIDのデバイスを検索 | | // 元のIDのデバイスを検索 |
| if (DX2_Ping (dev, id1, NULL)) { | | if (DX2_Ping (dev, id1, NULL)) { |
- | // 変更先のIDのデバイスを検索 | + | // 変更先のIDを持つデバイスが無い事を確認 |
| if (!DX2_Ping (dev, id2, NULL) && !DX2_Ping (dev, id2, NULL)) { | | if (!DX2_Ping (dev, id2, NULL) && !DX2_Ping (dev, id2, NULL)) { |
- | printf ("Change ID %d->%d ", id1, id2); | + | printf ("Change ID %d -> %d ...", id1, id2); |
- | // トルクディスエーブル(必須) | + | // トルクディスエーブル |
| DX2_WriteByteData (dev, id1, _TORQUE_ENABLE, 0, NULL); | | DX2_WriteByteData (dev, id1, _TORQUE_ENABLE, 0, NULL); |
| // 新しいIDを書き込み | | // 新しいIDを書き込み |
| printf ("ID(%d) is in conflict\n", id2); | | printf ("ID(%d) is in conflict\n", id2); |
| } else | | } else |
- | printf ("Cannot find ID(%d)\n", id1); | + | printf ("Couldnot find ID(%d)\n", id1); |
| + | DX2_ClosePort (dev); |
| } else | | } else |
- | printf ("Unnecessary to change\n"); | + | printf ("Couldnot open COM port.\n"); |
- | DX2_ClosePort (dev); | + | } else |
- | } | + | printf ("Unnecessary to change\n"); |
| } | | } |
| </pre> | | </pre> |