Skip to main content

HighTorqueシリーズモーターユーザーマニュアル

HighTorqueシリーズモータークイックスタートガイド

この文書では、HighTorqueシリーズモーターの素早い開始方法を紹介します。

技術仕様

遊星関節モジュールパラメータ比較表

技術仕様ダウンロードHTDW-5047-36-NEHTDW-4438-32-NE
減速比3630
ピークトルク (Nm)166
定格トルク (Nm)41.5
ストールトルク (Nm)2410
定格速度 (RPM)4036
無負荷速度 (RPM)6075
定格電力 (W)1713
トルク定数 (Nm/A)0.0620.039
極対数14-
定格電圧 (V)12-4812-48
定格電流 (A)21
ピーク電流 (A)105
トルク制御精度±10%±20%
速度制御精度±8%±10%
応答時間 (μs)≤200≤200
高速エンコーダ分解能14bit14bit
低速エンコーダ分解能12bit12bit
通信ボーレート (Mbps)55
制御周波数 (Hz)3k3k

モーター取り付け寸法

  • HTDM-4438-32:
  • HTDM-5047-36:

Windows PC準備

プロトコル解析

プロトコル解析

1.1 CAN関連指示

  1. CANボーレート:
    • 調停セグメント: 1 Mbps
    • データセグメント: 1 Mbps
  2. ID: 16ビットで構成され、0x7Fはブロードキャストアドレスです。
    • 上位8ビット: ソースアドレスを表す:
      • 最上位ビットが1: 応答が必要で、マスタースイッチとして機能します。クエリコマンドを送信せずに有効にすると、データセグメント長が0のフレームが返されます。
      • 最上位ビットが0: 応答は不要です。
      • 残りの7ビット: 信号ソースアドレス。
    • 下位8ビット: 宛先アドレスを表す:
      • 最上位ビットは0。
      • 残りの7ビットは宛先アドレスを表します。

例:

  1. ID: 0x8001
    • 信号ソースアドレスは0。
    • 宛先アドレスは1。
    • 最上位ビットが1で、応答が必要であることを示し、つまり応答マスタースイッチがオンになっています。
  2. ID: 0x100
    • 信号ソースアドレスは1。
    • 宛先アドレスは0。
    • 最上位ビットが0で、応答が不要であることを示し、つまり応答マスタースイッチがオフになっています。

1.2 モード指示

1.2.1 通常モード(位置と速度は同時に制御できません)

uint8_t cmd[] = {0x07, 0x07, pos1, pos2, val1, val2, tqe1, tqe2};

通常プロトコルは、コマンドビット(2バイト)+ 位置(2バイト)+ 速度(2バイト)+ トルク(2バイト)で構成され、合計8バイトです。

0x07 0x07: 通常モードで、速度とトルク、または位置とトルクを制御できます([[#2.1-通常モード]]を参照)。

プロトコル内の位置、速度、トルクデータはリトルエンディアンモードで、つまり下位バイトが最初に送信され、その後上位バイトが送信されます。例えば、pos = 0x1234の場合、pos1 = 0x34、pos2 = 0x12となります。

このモードは2つの制御方法に分けることができます:

  • 位置とトルク制御(この時、val = 0x8000で制限なしを示します)。
  • 速度とトルク制御(この時、pos = 0x8000で制限なしを示します)。

1.2.2 トルクモード

uint8_t cmd[] = {0x05, 0x13, tqe1, tqe2};

トルクモードプロトコルは、コマンドビット(2バイト)+ トルク(2バイト)で構成されます。

0x05 0x13: 純粋なトルクモードで、その後に2バイトのトルクデータが続きます([[#2.3-トルクモード]]を参照)。

プロトコル内のトルクデータはリトルエンディアンモードで、つまり下位バイトが最初に送信され、その後上位バイトが送信されます。例えば、tqe = 0x1234の場合、tqe1 = 0x34、tqe2 = 0x12となります。

1.2.3 協調制御モード(位置、速度、トルクを同時に制御可能)

uint8_t cmd[] = {0x07, 0x35, val1, val2, tqe1, teq2, pos1, pos2};

協調制御モードプロトコル: コマンドビット(2バイト)+ 速度(2バイト)+ トルク(2バイト)+ 位置(2バイト)、合計8バイト。

0x07 0x35: 協調制御モードで、指定された速度で指定された位置まで回転し、最大トルクを制限します。

このモードでは、パラメータ0x8000を使用すると制限なしを示します(速度とトルクに制限がないということは最大値を意味します)。例えば、val = 5000、tqe = 1000、pos = 0x8000: モーターが毎秒0.5回転の速度で回転し、最大トルクが0.1 NMであることを意味します。

プロトコル内の位置、速度、トルクデータはすべてリトルエンディアンモードで、つまり下位バイトが最初に送信され、その後上位バイトが送信されます。例えば、pos = 0x1234の場合、pos1 = 0x34、pos2 = 0x12となります。

1.3 モーター状態データ読み取り

  1. モーター状態部分を読み取るプロトコルはCAN-FDのプロトコルと同じで、唯一の違いはCANが8バイトのデータセグメントに制限されることです。
  2. レジスタアドレスと機能指示については、「Register Function, Motor Operation Mode, Error Code Instructions.xlsx」ファイルを参照してください。
  3. CANの8バイトデータセグメント制限により、単一のCANフレームで返せるモーター情報は限られています:
    • レジスタ内の1つのfloat型またはint32_t型モーター情報。
    • 3つの連続したint16_t型モーター情報。
    • 6つの連続したint8_t型モーター情報。
  4. サンプルプログラムでは、int16_t型のモーター位置、速度、トルク情報を照会するサンプル関数とモーター情報解析を提供しています(サンプルプログラムではC言語のunionを使用してCANの3番目から8番目のバイトのデータを直接コピーします)。

1.3.1 送信プロトコル指示

uint8_t tdata[] = {cmd, addr, cmd1, addr1, cmd2, add2};

一般的な意味は: addrからcmd[3, 2]型のcmd[0, 1]個のデータを読み取る。

cmd:

  • 上位4ビット[7, 4]: 0001は読み取りを示します。
  • ビット2-3 [3, 2]: 型を示します。
    • 00: int8_t型。
    • 01: int16_t型。
    • 10: int32_t型。
    • 11: float型。
  • 下位2ビット[1, 0]: 数量を示します。
    • 01: 1つのデータ。
    • 10: 2つのデータ。
    • 11: 3つのデータ。

addr: 取得する開始アドレス。

複数のcmdとaddrを連結して、不連続なアドレスと異なる型のデータを一度に読み取ることができます。

1.3.2 受信プロトコル指示

取得したデータがuint16_tであると仮定します。

uint8_t rdata[] = {cmd, addr, a1, a2, b1, b2, ..., cmd1, addr1, c1, c2, c3, c4}

cmd:

  • 上位4ビット[7, 4]: 0010は応答を示します。
  • ビット2-3 [3, 2]: 型を示します。
    • 00: int8_t型。
    • 01: int16_t型。
    • 10: int32_t型。
    • 11: float型。
  • 下位2ビット[1, 0]: 数量を示します。
    • 01: 1つのデータ。
    • 10: 2つのデータ。
    • 11: 3つのデータ。

addr: 取得する開始アドレス。

a1, a2: データ1、リトルエンディアンモード。

b1, b2: データ2、リトルエンディアンモード。

1.3.3 例

  1. 位置、速度、トルクデータを読み取る必要があります。
  2. レジスタExcelテーブルから、位置、速度、トルクのデータアドレスが01、02、03であることがわかります。
  3. これから、アドレス01から3つの連続したデータを読み取ることができることがわかります。CANが一度に最大8バイトのデータを送信でき、cmd + addrが2バイトを占めることを考慮すると、データ型は最大でint16_t型になります。
  4. 上記から、cmdのバイナリは0001 0111で、16進数は0x17です。
  5. アドレス01から読み取りを開始する必要があるため、addrは0x01です。
  6. 送信する総データはuint8_t tdata[] = 1です。

サンプルコードは以下の通りです:

/**
* @brief Read the motor
* @param id
*/
void motor_read(uint8_t id)
{
static uint8_t tdata[8] = {0x17, 0x01};
CAN_Send_Msg(0x8000 | id, tdata, sizeof(tdata));
}

uint8_t cmd[] = {0x17, 0x01};

全体的な意味は:アドレス0x01から開始して3つのint16_tレジスタを読み取る(表によると、アドレス0x01から0x03のレジスタはそれぞれ位置、速度、トルクを表す)。したがって、このコマンドはモーターの位置、速度、トルク情報を照会するためのものです。

0x17:0x17[7:4]のバイナリは0001:読み取りを示す。0x17[3:2]のバイナリは01:データタイプがint16_tであることを示す。0x17[1:0]のバイナリは11:データ数が3であることを示す。0x01:アドレス0x01から開始。

対応する受信データの例:

uint8_t rdata[] = {0x27, 0x01, 0x38, 0xf6, 0x09, 0x00, 0x00, 0x00};

0x27:送信された0x17に対応。0x01:アドレス0x01から開始。0x38 0xf6:位置データ:0xf638、すなわち-2505。0x09 0x00:速度データ:0x0009、すなわち9。0x00 0x00:トルクデータ:0x0000、すなわち0。

1.4 モーター停止

説明:

  1. モーターを停止します。
  2. ホストコンピューター命令d stopに対応します。
/**
* @brief Stop the motor
*/
void motor_stop(uint8_t id)
{
uint8_t tdata[] = {0x01, 0x00, 0x00};
CAN_Send_Msg(0x8000 | id, tdata, sizeof(tdata));
}

1.5 モーターゼロ位置リセット

説明:

  1. 現在の位置をモーターのゼロ位置として設定します。
  2. この命令はRAM内でのみ変更し、conf write命令と組み合わせてflashに保存する必要があります。
  3. この命令を使用した約1秒後にconf write命令を送信することを推奨します。
void rezero_pos(uint8_t id)
{
uint8_t tdata[] = {0x40, 0x01, 0x04, 0x64, 0x20, 0x63, 0x0a};
CAN_Send_Msg(0x8000 | id, tdata, sizeof(tdata));
HAL_Delay(1000); // It is recommended to delay for 1s
conf_write(id); // Save the settings
}

1.6 モーター設定保存(conf write)

説明:

  1. RAM内のモーター設定をflashに保存します。
  2. この命令を使用した後、モーターの電源を再投入することを推奨します。
void conf_write(uint8_t id)
{
uint8_t tdata[] = {0x05, 0xb3, 0x02, 0x00, 0x00};
CAN_Send_Msg(0x8000 | id, tdata, sizeof(tdata));
}

1.7 モーターステータス読み取り

説明:

  1. モーターの位置、速度、トルクデータを一度読み取ります。
  2. モーターフィードバックステータス情報データの解析については、例の割り込み関数HAL_FDCAN_RxFifo0Callbackのコードを参照してください。
* @brief Instruction to read motor position, speed, and torque
* @param id Motor ID
*/
void motor_read(CAN_HandleTypeDef *hcan, uint8_t id)
{
static uint8_t tdata[8] = {0x17, 0x01};
can_send(hcan, 0x8000 | id, tdata, sizeof(tdata));
}

1.8 モーターステータスデータの定期返送

説明:

  1. モーターの位置、速度、トルクデータを定期的に返送します。
  2. 返送されるデータ形式は、0x17、0x01命令を使用して取得したものと同じです(すなわち、1.7位置ステータス読み取り)。
  3. 周期の単位はmsです。
  4. 最小周期は1msです。
  5. データの定期返送を停止するには、周期を0に設定するか、モーターの電源を切ってください。
  6. モーターフィードバックステータス情報データの解析については、例の割り込み関数HAL_FDCAN_RxFifo0Callbackのコードを参照してください。
void timed_return_motor_status(uint8_t id, int16_t t_ms)
{
uint8_t tdata[] = {0x05, 0xb4, 0x02, 0x00, 0x00};
*(int16_t *)&tdata[3] = t_ms;
CAN_Send_Msg(0x8000 | id, tdata, sizeof(tdata));
}

2. サンプル関数

2.1 ノーマルモード

2.1.1 位置制御

/**
* @brief Position control
* @param id Motor ID
* @param pos Position: Unit 0.0001 circle, e.g., pos = 5000 means rotating to the position of 0.5 circle.
* @param torque
*/
void motor_control_Pos(uint8_t id,int32_t pos,int16_t tqe)
{
uint8_t tdata[8] = {0x07, 0x07, 0x0A, 0x05, 0x00, 0x00, 0x80, 0x00};
*(int16_t *)&tdata[2] = pos;
*(int16_t *)&tdata[6] = tqe;
uint32_t ext_id = (0x8000 | id);
CAN_Send_Msg(ext_id, tdata, 8);
}

2.1.2 速度制御

/**
* @brief Speed control
* @param id Motor ID
* @param vel Speed: Unit 0.00025 rotations/second, e.g., val = 1000 means 0.25 rotations/second
* @param tqe Torque
*/
uint8_t tdata[8] = {0x07, 0x07, 0x00, 0x80, 0x20, 0x00, 0x80, 0x00};
*(int16_t *)&tdata[4] = vel;
*(int16_t *)&tdata[6] = tqe;
uint32_t ext_id = (0x8000 | id);
CAN_Send_Msg(ext_id, tdata, 8);
}

2.3 トルクモード

/**
* @brief Torque mode
* @param id Motor ID
* @param tqe Torque
*/
void motor_control_tqe(uint8_t id,int32_t tqe)
{
uint8_t tdata[8] = {0x05, 0x13, 0x00, 0x80, 0x20, 0x00, 0x80, 0x00};
*(int16_t *)&tdata[2] = tqe;
CAN_Send_Msg(0x8000 | id, tdata, 4);
}

2.4 協調制御モード

/**
* @brief Motor position-speed-feedforward torque (maximum torque) control, int16 type
* @param id Motor ID
* @param pos Position: Unit 0.0001 circle, e.g., pos = 5000 means rotating to the position of 0.5 circle.
* @param val Speed: Unit 0.00025 rotations/second, e.g., val = 1000 means 0.25 rotations/second
* @param tqe Maximum torque
*/
void motor_control_pos_val_tqe(uint8_t id, int16_t pos, int16_t val, int16_t tqe)
{
static uint8_t tdata[8] = {0x07, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
*(int16_t *)&tdata[2] = val;
*(int16_t *)&tdata[4] = tqe;
*(int16_t *)&tdata[6] = pos;
CAN_Send_Msg(0x8000 | id, tdata, 8);
}

2.5 DQ電圧モード

説明:

  1. Q相電圧を制御できます。単位:0.1v、例:vol = 10はQ相電圧が1Vであることを意味します。
void motor_control_volt(FDCAN_HandleTypeDef *hfdcanx, uint8_t id, int16_t vol)
static uint8_t tdata[7] = {0x01, 0x00, 0x08, 0x05, 0x1b, 0x00, 0x00};
*(int16_t *)&tdata[5] = vol;
can_send(hfdcanx, 0x8000 | id, tdata, sizeof(tdata));
}

2.6 DQ電流モード

説明:

  1. Q相電流を制御できます。単位:0.1A、例:cur = 10はQ相電圧が1Aであることを意味します。
void motor_control_cur(FDCAN_HandleTypeDef *hfdcanx, uint8_t id, int16_t cur)
{
static uint8_t tdata[7] = {0x01, 0x00, 0x09, 0x05, 0x1c, 0x00, 0x00};
*(int16_t *)&tdata[5] = cur;
can_send(hfdcanx, 0x8000 | id, tdata, sizeof(tdata));
}

2.7 ブレーキ

説明:

  1. モーターブレーキ、モーターを回転させるとダンピングが発生します。
/**
* @brief Motor brake
* @param fdcanHandle &hfdcanx
* @param motor id Motor ID
*/
void set_motor_brake(FDCAN_HandleTypeDef *fdcanHandle, uint8_t id)
static uint8_t cmd[] = {0x01, 0x00, 0x0f};
can_send(fdcanHandle, 0x8000 | id, cmd, sizeof(cmd));
}

2.8 停止

説明:

  1. モーターが停止し、位置を維持する力を失います。
/**
* @brief Stop the motor. Note: The motor must be stopped before resetting the zero position, otherwise it will be invalid.
* @param fdcanHandle &hfdcanx
* @param motor id Motor ID
*/
void set_motor_stop(FDCAN_HandleTypeDef *fdcanHandle, uint8_t id)
{
static uint8_t cmd[] = {0x01, 0x00, 0x00};
can_send(fdcanHandle, 0x8000 | id, cmd, sizeof(cmd));
}

3. 一般的なタイプの説明(単位)

3.1 電流(A)

データタイプLSB実際(A)
int811
int1610.1
int3210.001
float11

3.2 電圧(V)

データタイプLSB実際(V)
int810.5
int1610.1
int3210.001
float11

3.3 トルク(Nm)

真のトルク = k * tqe + d

3.3.1 5046 トルク(Nm)

データタイプ傾き(k)オフセット(d)
int160.005397-0.455107
int320.000528-0.414526
float0.533654-0.519366

3.3.2 4538 トルク(Nm)

データタイプ傾き(k)オフセット(d)
int160.004587-0.290788
int320.000445-0.234668
float0.493835-0.473398

3.3.2 5047/6056(バイポーラ、36ギア比)トルク(Nm)

データタイプ傾き(k)オフセット(d)
int160.004563-0.493257
int320.000462-0.512253
float0.465293-0.554848

3.3.3 5047(ユニポーラ、9ギア比)トルク(Nm)

データタイプ傾き(k)オフセット(d)
int160.005332-0.072956
int320.000533-0.034809
float0.547474-0.150232

3.4 温度(℃)

データタイプLSB実際(℃)
int811
int1610.1
int3210.001
float11

3.5 時間(s)

データタイプLSB実際(s)
int810.01
int1610.001
int3210.000001
float11

3.6 位置(回転数)

データタイプLSB実際(回転数)実際(°)
int810.013.6
int1610.00010.036
int3210.000010.0036
float11360

3.7 速度(回転数/秒)

データタイプLSB実際(回転数/秒)
int810.01
int1610.00025
int3210.00001
float11

3.8 加速度(回転数/秒²)

データタイプLSB実際(回転数/秒²)
int810.05
int1610.001
int3210.00001
float11

3.9 PWMスケール(無単位)

データタイプLSB実際
int811/127 - 0.007874
int1611/32767 - 0.000030519
int321(1/2147483647) - 4.657^10
float11

3.10 Kp、Kdスケール(無単位)

データタイプLSB実際
int811/127 - 0.007874
int1611/32767 - 0.000030519
int321(1/2147483647) - 4.657^10
float11

C++の例

C++制御には追加のCAN-USBドライバーボードが必要です。livelybot_hardware_sdkを参照してください

reComputer Mini Jetson Orinでモーターを制御する

現在、市場でモーターに最も一般的に使用されているCAN通信インターフェースは、XT30(2+2)およびJSTコネクタです。私たちのreComputer Mini Jetson OrinおよびreComputer Roboticsデバイスには、デュアルXT30(2+2)ポートとJSTベースのCANインターフェースが搭載されており、シームレスな互換性を提供します。

reComputer Mini:

reComputer Robotics

CANの使用に関する詳細については、このwikiを参照してください。

CANインターフェースの有効化

ステップ1: CAN0とCAN1を使用する前に、底面カバーを取り外し、両方の120Ω終端抵抗をON位置に設定してください。

ステップ2: XT30(2+2)インターフェースを介してモーターをreComputer MiniのCAN0に直接接続してください。

tip

reComputer MiniのCANインターフェースのH/Lピンはモーターのものと逆になっているため、XT30 2+2ハーネスのH/L接続を逆にする必要があります。

danger

この電源ソリューションは、単一モーターの学習とテストにのみ適しています。マルチモーターアプリケーションの場合は、Jetsonの電源供給をモーターの電源供給から分離し、大電流がJetsonを直接通過することを避けるために、独立した電源ボードを設計してください。

Jetson CAN通信の有効化

ターミナルを開き、以下のコマンドを入力してGPIOピンをハイにしてCAN0を有効化してください:

gpioset --mode=wait 0 43=0

JSTインターフェースのCAN1を使用する場合は、ピン106をハイにしてください:

gpioset --mode=wait 0 106=0

このターミナルを開いたままにして、新しいターミナルを作成してCAN0を設定してください:

sudo modprobe mttcan
sudo ip link set can0 type can bitrate 1000000
sudo ip link set can0 up

Python制御

  • Python環境のインストール
pip install python-can numpy
  • スクリプトディレクトリの作成
mkdir -p ~/hightorque/scripts
  • hightorque_motor.pyファイルの作成
cd ~/hightorque/scripts
touch hightorque_motor.py

以下のコードをhightorque_motor.pyにコピーしてください。

hightorque_motor.py
import can
import numpy as np
from time import sleep
from enum import IntEnum

class MotorType(IntEnum):
"""Motor Type Enum"""
HT5046 = 0 # 5046 Motor
HT4538 = 1 # 4538 Motor
HT5047_36 = 2 # 5047/6056 Dual-pole 36 Reduction Ratio
HT5047_9 = 3 # 5047 Single-pole 9 Reduction Ratio

class ControlMode(IntEnum):
"""Control Mode Enum"""
NORMAL = 0 # Normal Mode
TORQUE = 1 # Torque Mode
COOPERATIVE = 2 # Cooperative Control Mode

class Motor:
def __init__(self, motor_type: MotorType, slave_id: int, master_id: int):
"""
Initialize Motor Object
:param motor_type: Motor Type
:param slave_id: Slave ID
:param master_id: Master ID
"""
self.motor_type = motor_type
self.slave_id = slave_id
self.master_id = master_id
self.position = 0
self.velocity = 0
self.torque = 0
self.temperature = 0

# Set Torque Conversion Parameters Based on Motor Type
if motor_type == MotorType.HT5046:
self.torque_k = 0.005397
self.torque_d = -0.455107
elif motor_type == MotorType.HT4538:
self.torque_k = 0.004587
self.torque_d = -0.290788
elif motor_type == MotorType.HT5047_36:
self.torque_k = 0.004563
self.torque_d = -0.493257
elif motor_type == MotorType.HT5047_9:
self.torque_k = 0.005332
self.torque_d = -0.072956

def update_status(self, position: float, velocity: float, torque: float, temperature: float):
"""Update Motor Status"""
self.position = position
self.velocity = velocity
self.torque = torque
self.temperature = temperature

class MotorControl:
def __init__(self, channel: str, bitrate: int = 1000000):
"""
Initialize Motor Controller
:param channel: CAN Channel
:param bitrate: CAN Baud Rate
"""
self.bus = can.interface.Bus(channel=channel, bustype='socketcan', bitrate=bitrate)
self.motors = {}

def add_motor(self, motor: Motor):
"""Add Motor to Controller"""
self.motors[motor.slave_id] = motor

def __send_data(self, motor_id: int, data: bytes):
"""
Send CAN Data
:param motor_id: Motor ID
:param data: Data to Send
"""
msg = can.Message(
arbitration_id=0x8000 | motor_id,
data=data,
is_extended_id=True
)
self.bus.send(msg)

def enable(self, motor: Motor):
"""Enable Motor"""
data = bytes([0x01, 0x00, 0x01])
self.__send_data(motor.slave_id, data)
sleep(0.1)

def disable(self, motor: Motor):
"""Disable Motor"""
data = bytes([0x01, 0x00, 0x00])
self.__send_data(motor.slave_id, data)
sleep(0.1)

def set_zero_position(self, motor: Motor):
"""Set Motor Zero Position"""
data = bytes([0x40, 0x01, 0x04, 0x64, 0x20, 0x63, 0x0a])
self.__send_data(motor.slave_id, data)
sleep(1.0) # Wait 1 second
self.save_settings(motor)

def save_settings(self, motor: Motor):
"""Save Motor Settings to Flash"""
data = bytes([0x05, 0xb3, 0x02, 0x00, 0x00])
self.__send_data(motor.slave_id, data)

def control_position(self, motor: Motor, position: float, torque: float):
"""
Position Control
:param motor: Motor Object
:param position: Target Position (Unit: 0.0001 turns)
:param torque: Torque Limit
"""
pos_bytes = int(position).to_bytes(2, 'little')
tqe_bytes = int(torque).to_bytes(2, 'little')
data = bytes([0x07, 0x07]) + pos_bytes + bytes([0x80, 0x00]) + tqe_bytes
self.__send_data(motor.slave_id, data)

def control_velocity(self, motor: Motor, velocity: float, torque: float):
"""
Velocity Control
:param motor: Motor Object
:param velocity: Target Velocity (Unit: 0.00025 turns/second)
:param torque: Torque Limit
"""
vel_bytes = int(velocity).to_bytes(2, 'little')
tqe_bytes = int(torque).to_bytes(2, 'little')
data = bytes([0x07, 0x07, 0x00, 0x80]) + vel_bytes + tqe_bytes
self.__send_data(motor.slave_id, data)

def control_torque(self, motor: Motor, torque: float):
"""
Torque Control
:param motor: Motor Object
:param torque: Target Torque
"""
tqe_bytes = int(torque).to_bytes(2, 'little')
data = bytes([0x05, 0x13]) + tqe_bytes
self.__send_data(motor.slave_id, data)

def control_cooperative(self, motor: Motor, position: float, velocity: float, torque: float):
"""
Cooperative Control (Position, Velocity, Torque Simultaneous Control)
:param motor: Motor Object
:param position: Target Position (Unit: 0.0001 turns)
:param velocity: Target Velocity (Unit: 0.00025 turns/second)
:param torque: Torque Limit
"""
vel_bytes = int(velocity).to_bytes(2, 'little')
tqe_bytes = int(torque).to_bytes(2, 'little')
pos_bytes = int(position).to_bytes(2, 'little')
data = bytes([0x07, 0x35]) + vel_bytes + tqe_bytes + pos_bytes
self.__send_data(motor.slave_id, data)

def read_motor_status(self, motor: Motor):
"""Read Motor Status"""
data = bytes([0x17, 0x01])
self.__send_data(motor.slave_id, data)
sleep(0.01) # Wait for Data Reception

# Receive and Parse Data
msg = self.bus.recv(timeout=0.1)
if msg and msg.arbitration_id == (0x8000 | motor.slave_id):
data = msg.data
if len(data) >= 8 and data[0] == 0x27:
position = int.from_bytes(data[2:4], 'little')
velocity = int.from_bytes(data[4:6], 'little')
torque = int.from_bytes(data[6:8], 'little')
motor.update_status(position, velocity, torque, 0)

def periodic_read_status(self, motor: Motor, period_ms: int):
"""
Set Periodic Motor Status Reading
:param motor: Motor Object
:param period_ms: Period (milliseconds)
"""
period_bytes = int(period_ms).to_bytes(2, 'little')
data = bytes([0x05, 0xb4, 0x02, 0x00]) + period_bytes
self.__send_data(motor.slave_id, data)

def close(self):
"""Close CAN Bus"""
self.bus.shutdown()
  • hightorque_test.pyファイルの作成

以下のコードをhightorque_test.pyにコピーしてください。

hightorque_test.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
import math
import numpy as np
from hightorque_motor import Motor, MotorControl, MotorType

# Configuration Parameters
NUM_MOTORS = 2 # Number of Motors to Control
CAN_INTERFACE = "can0" # CAN Interface Name
CAN_BITRATE = 1000000 # CAN Baud Rate
MOTOR_TYPE = MotorType.HT5047_36 # Motor Type

# Sine Wave Parameters
FREQUENCY = 0.1 # Frequency (Hz)
AMPLITUDE = 2500 # Amplitude (0.0001 turns)
OFFSET = 2500 # Offset to Ensure Positive Position
DURATION = 60.0 # Run Duration (s)

def main():
# Create Motor Control Object
controller = MotorControl(channel=CAN_INTERFACE, bitrate=CAN_BITRATE)

try:
# Create and Add Motors
motors = []
for i in range(NUM_MOTORS):
motor = Motor(MOTOR_TYPE, slave_id=i+1, master_id=0)
controller.add_motor(motor)
motors.append(motor)

# Enable Motor
print(f"Enabling Motor {i+1}...")
controller.enable(motor)
time.sleep(1) # Wait for Motor Enable

# Set Zero Position
print(f"Setting Motor {i+1} Zero Position...")
controller.set_zero_position(motor)
time.sleep(1)

# Save Settings to Flash
print(f"Saving Motor {i+1} Settings...")
controller.save_settings(motor)
time.sleep(1)

# Read Initial Status
controller.read_motor_status(motor)
print(f"Motor {i+1} Initial Status:")
print(f"Position: {motor.position * 0.0001:.4f} turns")
print(f"Velocity: {motor.velocity * 0.00025:.4f} turns/second")
print(f"Torque: {motor.torque * motor.torque_k + motor.torque_d:.4f} Nm")

# Start Sine Wave Position Control
print("\nStarting Sine Wave Position Control...")
start_time = time.time()
while time.time() - start_time < DURATION:
current_time = time.time() - start_time

# Calculate Sine Wave Position with Offset to Ensure Positive
position = AMPLITUDE * math.sin(2 * math.pi * FREQUENCY * current_time) + OFFSET

# Control All Motors
for motor in motors:
# Use Position Control Mode with Max Torque of 1000
controller.control_position(motor, position=int(position), torque=1000)

# Control Frequency
time.sleep(0.001) # 1kHz Control Frequency

except KeyboardInterrupt:
print("\nProgram Interrupted by User")
finally:
# Disable All Motors
for motor in motors:
print(f"Disabling Motor {motor.slave_id}...")
controller.disable(motor)

# Close CAN Bus
controller.close()
print("CAN Bus Closed")

if __name__ == "__main__":
main()
  • hightorque_test.py を実行
python hightorque_test.py

技術サポートと製品ディスカッション

弊社製品をお選びいただき、ありがとうございます!最高の体験をしていただけるよう、様々なサポートチャンネルをご用意しております。

Loading Comments...