ブログ - 最新エントリー

KONDO PMX試食2

カテゴリ : 
雑記
2024-4-16 14:20
前回に引き続きPMX。

各装置の接続は最終的にこのような構成に。
キャリアボードに供給する電源が内蔵のスイッチ回路を介してEHコネクタからサーボへ供給される。
Raspberry Pi Zero 2 Wの操作はモニタとキーボードを直接接続して行うも良し、USB経由のRNDISでリモートログインしても良し、WiFi経由でリモートログインしても良し。
これでハードウェアの準備は完了。

ではようやくソフトウェアに。
Raspberry Pi Zero 2 WをホストとしたのでLinuxが前提、Zeroは極めてメモリが少ないのでCLIを基本とした。今回はモータが動きさえすれば良いので、プログラムのデバッグはキャラクタベースで十分と判断。
キャリアボードはRaspberry Piの機能を利用するので、本家のドキュメントDXHATの使用方法を元に設定。念のためcmdline.txtからconsoleの記述が消えているのを確認しておこう。
キャリアボードのRS-485 I/FはZeroの「/dev/ttyAMA0」に割り当てられる。
この辺は先人の知恵を借りれば良し。

次にPythonによるPMXとの通信だが、既にDXL用Pythonライブラリが公開されているのでその構成を真似ね、PMXのプロトコルで規定されているコマンドに応じた関数を用意し、こちらでpyPMX.pyとして公開。あると便利な検索・ID変更・ボーレート変更の各スクリプトも事のついでに作成。これにてオンラインマニュアル片手にポチポチと弄る準備が完了。
一応ライブラリの末尾にテストコードを仕込んでおいたが、PMXに位置決め制御を行わせるだけであれば、以下のように動作モードを設定しTorqueONした後にMotorWRITEで角度を指令するだけ。
#!/usr/bin/python3
import time
from pyPMX import PMXProtocol

ID = 0
BAUD = 3000000
# Create pmx instance
pmx = PMXProtocol('/dev/ttyAMA0', BAUD)

pmx.MemREAD(ID, 400, 6) # Error clear
pmx.MotorWRITE(ID, pmx.MOTW_OPT_FREE, ()) # Torque off
pmx.MemWRITE8(ID, 501, 1) # bit 0:pos,1:speed,2:cur,3:torq,4:pwm,5:time
pmx.MotorWRITE(ID, pmx.MOTW_OPT_TORQUEON, ()) # Torque on

for ang in (0, 4500, 9000, 4500, 0, -4500, -9000, -4500, 0):
pmx.MotorWRITE(ID, pmx.MOTW_OPT_NONE, (ang,)) # Command angle
time.sleep(0.5)
pmx.MotorWRITE(ID, pmx.MOTW_OPT_FREE, ()) # Torque off
実はこの程度の処理にもかかわらず、想定した動作を行ってくれない事が散見された。各関数の成否を確認しないまま次の処理に遷移させているため、初期設定の所で何かしら失敗したまま角度の指令がなされている時に動かなかった様だ。
それがキャリアボードの問題なのか通信方法の問題なのかの切り分けはしていないが、関数の返り値を確認してリトライする等の処理を加えれば、とりあえずお茶は濁せそうだ。

それらを意識した上で複数IDを一括で処置する関数を作成、少しだけコンソール上での操作をしやすくするためにkbhitを導入、最終的に2軸を対象にしたプログラムを作ってみた。
#!/usr/bin/python3

_retrynum = 3
# mode set & torque enable
def _setmode(pmx, id, m):
for i in range(_retrynum):
if pmx.MemWRITE8(id, 500, pmx.MOTW_OPT_FREE):
if m != 0:
# 1:pos,2:speed,4:cur,8:torq,16:pwm,32:time
if pmx.MemWRITE8(id, 501, m):
if pmx.MemWRITE8(id, 502, 0b11111):
if pmx.MemWRITE8(id, 503, 5):
if pmx.MemWRITE8(id, 500, pmx.MOTW_OPT_TORQUEON):
return True
else:
return True
return False

def SetMode(pmx, ids, m):
if isinstance(ids, list) or isinstance(ids, tuple):
r = 0
for i, id in enumerate(ids):
if isinstance(m, list) or isinstance(m, tuple):
r += 1 if _setmode(pmx, id, m[i]) else 0
else:
r += 1 if _setmode(pmx, id, m) else 0
return True if r == len(ids) else False
else:
return _setmode(pmx, ids, m)

# set angle
def _setangle(pmx, id, ang, t):
for i in range(_retrynum):
r = pmx.MotorWRITE(id, pmx.MOTW_OPT_NONE, (int(ang * 100), t))
if r != None:
if len(r) > 0:
return r[1]
return ()

def SetAngle(pmx, ids, angs, t):
if isinstance(ids, list) or isinstance(ids, tuple):
r = ()
for i, id in enumerate(ids):
if isinstance(angs, list) or isinstance(angs, tuple):
r += _setangle(pmx, id, angs[i], t),
else:
r += _setangle(pmx, id, angs, t),
return r
else:
return _setangle(pmx, ids, angs, t)

# set velocity
def _setvelocity(pmx, id, velo):
for i in range(_retrynum):
r = pmx.MotorWRITE(id, pmx.MOTW_OPT_NONE, (int(velo),))
if r != None:
if len(r) > 0:
return r[1]
return ()

def SetVelocity(pmx, ids, velos):
if isinstance(ids, list) or isinstance(ids, tuple):
r = ()
for i, id in enumerate(ids):
if isinstance(velos, list) or isinstance(velos, tuple):
r += _setvelocity(pmx, id, velos[i]),
else:
r += _setvelocity(pmx, id, velos),
return r
else:
return _setvelo(pmx, ids, velos)

def _getpresent(pmx, id):
for i in range(_retrynum):
r = pmx.MemREAD16(id, 300, 5, signed = True)
if r != None:
return r
return ()

def GetPresent(pmx, ids):
if isinstance(ids, list) or isinstance(ids, tuple):
r = ()
for i, id in enumerate(ids):
r += _getpresent(pmx, id),
return r
else:
return _getpresent(pmx, ids)

if __name__ == "__main__":
import os, sys, time, kbhit
from pyPMX import PMXProtocol

kb = kbhit.KBHit()
pmx = PMXProtocol('/dev/ttyAMA0', 3000000, 0.005)

ID1 = 0
ID2 = 1

# Position Control
print('Position Control')
print(f'{ID1}:angle velo cur torque pwm {ID2}:angle velo cur torque pwm')
if SetMode(pmx, (ID1, ID2), 1|32):
while not kb.kbhit():
t = 1000
for ang in (0, 45, 90, 45, 0, -45, -90, -45):
if kb.kbhit(): break
SetAngle(pmx, (ID1, ID2), (ang, ang * 2), t)
s = time.time() + t / 1000.0
while s > time.time():
if kb.kbhit(): break
r = GetPresent(pmx, (ID1, ID2))
for r in r:
print(('{:6d} '*len(r)).format(*r), end='')
print(end='\r')
SetMode(pmx, (ID1, ID2), 0)
kb.getch()

# Velocity Control
print('\nVelocity Control')
print(f'{ID1}:angle velo cur torque pwm {ID2}:angle velo cur torque pwm')
if SetMode(pmx, (ID1, ID2), 2):
while not kb.kbhit():
t = 3000
for velo in (0, 500, 1000, 1500, 1000, 500, 0, -500, -1000, -1500, -1000, -500):
if kb.kbhit(): break
SetVelocity(pmx, (ID1, ID2), (velo*1, velo*1.5))
s = time.time() + t / 1000.0
while s > time.time():
if kb.kbhit(): break
r = GetPresent(pmx, (ID1, ID2))
for r in r:
print(('{:6d} '*len(r)).format(*r), end='')
print(end='\r')
SetMode(pmx, (ID1, ID2), 0)

del kb, pmx
適当な指令値を元に位置決め制御と速度制御を行い、コンソール上で何か入力すると処理を推移する。制御中は読み出した現在値をバラバラとコンソールに吐き出し続ける。
紹介したコードはいらぬものも含んでいるので少々長くなっているが、SetModeを行って成功したらSetAngleやSetVelocityを行っているだけの事である。

Zeroを一通り操作している様子をラズパイカメラで自撮りしておいた。

ちなみに先の関数エラーはRS-485トランシーバの電気的な問題だったようで、真面目にターミネータの他にプルアップとプルダウン抵抗を装備して事なきを得た。

技術

KONDO PMX試食1

カテゴリ : 
雑記
2024-4-13 10:10
リリースから多少時間が経過した頃合いを見計らって、KONDO PMXシリーズに触れてみた。
今回は具体的に何かしらのカタチをなすものではなく、試作機や通信方式、Pythonといったところの評価を兼ねての事だが。
改めてPMXがどういった素性を持っているかを紹介するまでもないが、今回評価するにあたり必要な点を挙げておくと、
  • RS-485 I/F装備
  • 専用プロトコルを装備
  • 1つのI/Fのみで複数台を制御可能
  • 3Mbpsまでの通信速度に対応
  • 物理値を元にするのでモデルによる差異を意識することは無い
といったところで、Dynamixelで謳っている事と大きな差異はないが、指令やフィードバックが物理値という所が琴線に触れる。電気的には既存のI/Fを流用することもできるだろうし、プログラムとしての概念も自分の認識と大きく乖離するものでもなさそうだ。

PMXのコネクタは手持ちのI/Fとは何ら互換性が無いので、EHコネクタへ変換するだけのケーブルを用意。

これでPMX絡みの準備は終了。

Raspberry Pi Zero用に試作したキャリアボードの評価も兼ねているので、XC330-T181-TがつながるようにRS-485からTTLに変換するケーブルを準備。

最後にキャリアボード。DXHATPicoSHIELDのあいのこのようなもの。

事のついでにテスト中の短絡事故を防ぐ目的で、Raspberry Pi Zero 2 Wとキャリアボード全体を覆うエンクロージャを3Dプリンタで出力。

なんだかminiPCの様相を呈している。

今回はここまで。


技術

Controlling Dynamixel with Python

カテゴリ : 
Dynamixel
2024-1-9 10:20
大勢を占めつつあるPython、多勢に無勢というのであれば全部Pythonで書いてしまえば前準備も端折れるという事で追記しました。
ちなみにデバッグに支障があるでしょうから、例外はクラスの中ではトラップしていません。

技術

PicoSHIELD

カテゴリ : 
新商品
2023-8-2 9:30
先日行われたこちらのイベントで紹介されたRaspberry Pi Picoを使った簡易的なオーディオプレーヤー。
いくつか提供を開始しましたので、ひとまずの使い方を紹介したページを作成しました。
また使用しているPicoSHIELDについては近々販売を開始します。

技術

Arduino UNO R4とDXSHIELD

カテゴリ : 
Dynamixel
2023-7-5 10:50
いくつか問い合わせをいただいている「Arduino UNO R4 MinimaDXSHIELDで利用できるか」ですが、実際に使ってみました。


まずArduino Uno Rev3と形状が同一なので装着は可、端子機能は互換性がある、プログラムはAVRとARMの違いがあるが想定の範囲、といった程度で考えていました。
しかし電気的には問題はありませんでしたが、いざプログラムを実行してみると、ほぼ何もできない状態に直面しました。
結果から書いてしまうと、Minima用のライブラリが過度な高い通信速度や負荷に耐えられるレベルに至っていないため、現時点では両手を挙げて使えるとは言いがたい状態でした。
昨今の初物ではありがちな状態でしたので、そのうち改修されていくものと思いますが、少なくともUARTに過度な期待をしないのが得策です。
とりあえずMinima用のライブラリを使う場合は115.2kbps以下であれば使えますし、e2stdudioでフルスクラッチすれば普通に頑張れます。

技術

Arduino向けライブラリ更新

カテゴリ : 
マイコンボード
2023-3-15 10:20
DXSHIELD向けライブラリ改め、Arduino向けライブラリを更新しました。
Arduinoの 大勢がAVR系から他のコアになりつつあるのと、そういったケースに適用する際の修正はユーザに委ねていました。そこで今回からAVR想定だった通信部分を、完全に外出しにする事にしました。
以前との差異は以下の通りです。
  • クラスのインスタンスの際にシリアル通信にかかるハンドラをとりまとめた構造体を指定
  • AVR用として提供しているサンプルスケッチには、ハードウェアシリアル及びソフトウェアシリアルを使用したハンドラを用意
    Arduino環境であれば他のターゲットでもほぼ同じハンドラが利用可
  • ライブラリをAVR以外に適用すると、PC用DXLIBにも含まれている追加APIが利用可(β版扱い)
暗に新製品を想定している事を匂わせています

技術

Pico

カテゴリ : 
雑記
2023-2-20 13:20
あまり目新しい事ができていないので、合間を見つけて手持ちのRaspberry Pi Pico用にこんなものをあつらえてみました。


板のサイズをPicoと同じにした都合、RS-485しか装備できず。余ったスペースにはオーディオアンプとレギュレータしか載ってません。
ひとまずXL430-W250とつなぐためにI/F変換をかませました。
試したところDXSHIELD用のライブラリは無改造で使えました。残念なのはボーレートが3Mbpsまでしか設定できない事と、USBとRS-485間のブリッジをこさえた際に気付いたのがPC側のアプリがハードフローを活性化していないとダメといったところでしょうか。

技術

GCC Developer Liteの更新

カテゴリ : 
その他
2022-6-9 10:20
立て続けの更新のお知らせですが、GCC Developer Liteの最新バージョン(2022/6版)を公開しました。実質前バージョンとの差は大きくないため、該当する機能に琴線が触れない場合は更新の必要はありません。
  • 基本パック
    • GCCDevL.exeのエディタフォントサイズ変更用ショートカットキー(Ctrl+ +/-)追加
    • GCCDevL.exeのエディタにおいて対応する括弧へジャンプするショートカットキー(Ctrl+ [)追加
    • GCCDevL.exeのDEFファイル(各ターゲットの設定ファイル)中の文字列中にダブルクォーテーションを多用するようになったのでフェンスをカギ括弧に変更
      それに伴って全ターゲットのDEFファイルを変更する羽目に
    • STERM.exeのUSB挿抜イベント検出遅延を削除、コンポーネント更新に伴う再構築
      転送スクリプトが二重に励起される事がある原因は相変わらず不明
    • FW.exeのUSB挿抜イベント検出遅延を削除
      avrdudeのコマンドラインオプションを変更できるよう修正
    • 最新版msys2・OpenOCD・ctags適用
  • AVRパック
    • 設定リストのフォーマット変更に伴うDEFファイルの修正
    • 思うところあってArduinoをターゲットに含める
    • サンプルに支障があることを覚悟の上で全ターゲットのUARTルーチンを共通化
    • 最新版AVR TOOLCHAINを適用したのに合わせてAVRDUDE 7.0を同梱
    • FreePascal用のターゲットファイルを同梱(コンパイラと設定リストは別パックで提供予定)
  • ARMパック
    • 設定リストのフォーマット変更に伴うDEFファイルの修正
    • 根強い希望によりDX2LIBのclient機能にsync_read・sync_write・bulk_read・bulk_writeの各インストラクションへの応答を追加
    • DX2LIBの一部の追加APIの名称がWindows版を継承したままで使用できなかったのを修正
    • STM32F373用のターゲットファイルに含まれるST社のペリフェラルライブラリをV1.1に更新
  • SH/H8パック
    • 設定リストのフォーマット変更に伴うDEFファイルの修正のみ
  • Winパック
    • 最新のmsys2のバイナリ同梱

技術
DXSHIELD向けのライブラリを更新しました。
以前との差異は以下の通りです。
  • 原作者不在で放置されていたため全面的に精査
  • 一部チェックサムが無効化されていたのを修正
  • なぜか作られていなかったReadBlockData及びWriteBlockDataを追加
    それに伴いアイテムサイズに応じた読み出し及び書き込み関数は全て新設したBlock関数で代替
  • プロトコルV2における例外的な0xfdのサフィックス追加・除去機能を基本送受信関数内にて完全対応
  • いずれ追加される予定のArduino Nano RP2040 Connect用SHIELDでの検証を合わせて終了


技術
DXMIO with IMU向けのライブラリを更新しました。
以前との差異は以下の通りです。
  • フルカラーLEDやVL53L1Xの制御、DACを使ったサウンド再生、DXL追加APIのテスト、C++への対応方法のサンプルを追加
  • 近々リリースするDXMIO用I/Oボードへの対応(主にUSARTがらみ)

  • GCC Developer Liteを介さずにコンパイルできるようmakefileを追加
Dynamixelプロトコルで運用できるIMUとしてのサンプルには変更はありません。

技術