OPEN CR1.0 readControlTableItem 안됩니다

Issue: Open CR1.0, MX-64(2.0 protocol)

아래는 코드 일부입니다. 서보 TTL 연결하고 통신설정 다 해서 모터 동작은 원하는대로 잘 됩니다. 다만 readControlTableItem 값이 들어오질 않습니다. 왜그런걸까요?

#include “Dynamixel2Arduino.h”
#include “SPI.h”
#include “Adafruit_GFX.h” // 그래픽 라이브러리
#include “OpenCR_ILI9341.h” // ILI9341 디스플레이 라이브러리

// SPI 디스플레이 핀 설정
#define TFT_CS 10 // Chip Select 핀
#define TFT_DC 9 // Data/Command 핀
#define TFT_RST 8 // Reset 핀

// Servo Motor 통신 설정
#define DXL_SERIAL Serial3
#define DEBUG_SERIAL Serial
#define DXL_BAUDRATE 57600
const int DXL_DIR_PIN = 84; // OpenCR Board’s DIR PIN.

// 디스플레이 객체
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

//Servo 객체
Dynamixel2Arduino dxl(DXL_SERIAL, DXL_DIR_PIN);

//battery 상태 업데이트 시간차
const unsigned long interval = 1000; // 1분 = 60000ms
unsigned long previousMillis = 0;

int present_voltage=1;
#define PRESENT_VOLTAGE 144
#define CURRENT_LIMIT 38

// Servo Motor 회전 변환 단위 1rev = 4096분해능
#define DEGREE_TO_POSITION(d) ((d)*4096/360)
float servo_degrees[3];
int angle = 0;
int Direction = 1;

float s1 = 2.8-1.5;
float s2 = 2.8-2.5;
float s3 = 2.8-3.5;

//setup 영역------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {
DEBUG_SERIAL.begin(115200);
//Display 초기 세팅
tft.begin();
Display_UI();

//Servo 초기 세팅
DXL_SERIAL.begin(DXL_BAUDRATE);
dxl.begin();
dxl.setPortProtocolVersion(2.0);//Servo Protocol 2.0 설정
for (int i = 0; i < 3; i++) {
dxl.setOperatingMode(i, OP_VELOCITY); //Servo 속도 제어 모드
dxl.torqueOn(i);
}

if (dxl.ping(1)) {
DEBUG_SERIAL.println(“ID 1 모터와 통신 성공!”);
} else {
DEBUG_SERIAL.println(“ID 1 모터와 통신 실패!”);
}
delay(1000); // 1초 대기
}

//loop 영역------------------------------------------------------------------------------------------------------------------------------------------------------------
void loop() {
int16_t present_temperature = dxl.readControlTableItem(43, 0); // 현재 온도 주소
DEBUG_SERIAL.print("현재 온도: ");
DEBUG_SERIAL.println(present_temperature);
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // 이전 시간을 현재 시간으로 갱신
present_voltage = dxl.readControlTableItem(PRESENT_VOLTAGE, 0);
DEBUG_SERIAL.print("현재 전압 : ");
DEBUG_SERIAL.println(present_voltage);

PROVIDE A THOUROUGH DESCRIPTION OF YOUR ISSUE, THE MORE DETAIL YOU PROVIDE THE EASIER IT WILL BE FOR THE COMMUNITY TO PROVIDE YOU ASSISTANCE


DYNAMIXEL Servo:

LIST ALL DYNAMIXEL SERVOS USED IN THIS PROJECT


DYNAMIXEL Controller:

LIST ALL DYNAMIXEL CONTROLLERS USED IN THIS PROJECT


Software Interface:

LIST ALL SOFTWARE INTERFACES USED IN THIS PROJECT

안녕하세요.

readControlTableItem(uint8_t item_idx, uint8_t id) 입니다.

dxl.readControlTableItem(43, 0);
dxl.readControlTableItem(PRESENT_VOLTAGE, 0);

읽어오시는 정보는 id 0번의 다이나믹셀의 정보를 읽어 오고 있으며,
사용하시는 다이나믹셀의 id가 1번 이시면 readControlTableItem 0번이 아닌 1번으로 수정해주세요.

모터 아이디를 0, 1, 2로 설정해서 직렬연결을 한 상태입니다. 어느 아이디를 해봐도 전압값이 나오질 않습니다. 동작은 제대로 되는데…

  1. “Dynamixel2Arduino.h” 내용을 참고해주세요.

    • @brief It is API for getting data of a DYNAMIXEL control table item.
    • @code
    • const int DXL_DIR_PIN = 2;
    • Dynamixel2Arduino dxl(Serial1, DXL_DIR_PIN);
    • Serial.print(dxl.readControlTableItem(PRESENT_VOLTAGE, 1));
    • @endcode
    • @param item_idx DYNAMIXEL Actuator’s control table item’s index.
    • For each index, replace the name of the control table item in the e-manual with capital letters and replace the space with an underscore (_).
    • For the list of indexes, please refer to the link below.
    • TODO: add link (It is defined as ‘enum ControlTableItem’ in actuator.h)
    • @param id DYNAMIXEL Actuator’s ID.
    • @param timeout A timeout waiting for a response to a data transfer.
    • @return It returns the data read from DXL control table item.(Returns the value appropriate for @unit.)
    • If the read fails, 0 is returned. Whether or not this is an actual value can be confirmed with @getLastLibErrCode().
      */
      int32_t readControlTableItem(uint8_t item_idx,
      uint8_t id, uint32_t timeout = 100);
  2. Dynamixel2Arduino.h를 사용하시려면 아래 코드를 추가해서 사용해주세요.
    using namespace ControlTableItem;

  3. #define PRESENT_VOLTAGE 144 구문은 삭제 후
    dxl.readControlTableItem(PRESENT_VOLTAGE, 0);
    dxl.readControlTableItem(PRESENT_TEMPERATURE, 0);

위 처럼 사용하시면 정상적으로 읽기가 가능합니다.

주소값으로 입력을 원하시면, 예제 "read_x"를 참고해주세요.

하나하나 대조해본결과 PRESENT_VOLTAGE = 85, CURRENT_LIMIT = 57 인걸 알았습니다. E-Manual에 있는 주소와 왜 다를까요?

actuator.h에 나오는 번호 값은 enum ControlTableItemIndex 열거형에서의 인덱스 위치값입니다. 이 값은 해당 컨트롤 테이블 항목의 순서를 나타내며, 실제 컨트롤 테이블 주소값은 아닙니다.

실제 컨트롤 테이블 주소값actuator.cpp 파일에서 확인할 수 있으며, 해당 주소값을 사용하여 데이터를 읽거나 쓸 수 있습니다.

actuator.cpp에 있는 MX64 모델의 프로토콜 2.0 테이블 주소 값입니다.
input voltage는 144라고 되어 있는데 실제로는 85번주소로 불러올 수 있습니다. 이해가 안되서요.

const ModelControlTableInfo_t control_table_2_0 PROGMEM = {
#if (ENABLE_ACTUATOR_MX28_PROTOCOL2
|| ENABLE_ACTUATOR_MX64_PROTOCOL2
|| ENABLE_ACTUATOR_MX106_PROTOCOL2
|| ENABLE_ACTUATOR_XL330
|| ENABLE_ACTUATOR_XC330
|| ENABLE_ACTUATOR_XL430
|| ENABLE_ACTUATOR_XC430
|| ENABLE_ACTUATOR_XM430 || ENABLE_ACTUATOR_XH430 || ENABLE_ACTUATOR_XD430
|| ENABLE_ACTUATOR_XM540 || ENABLE_ACTUATOR_XH540 || ENABLE_ACTUATOR_XD540
|| ENABLE_ACTUATOR_XW540 || ENABLE_ACTUATOR_XW430)
{ControlTableItem::MODEL_NUMBER, 0, 2},
{ControlTableItem::MODEL_INFORMATION, 2, 4},
{ControlTableItem::FIRMWARE_VERSION, 6, 1},
{ControlTableItem::ID, 7, 1},
{ControlTableItem::BAUD_RATE, 8, 1},
{ControlTableItem::RETURN_DELAY_TIME, 9, 1},
{ControlTableItem::DRIVE_MODE, 10, 1},
{ControlTableItem::OPERATING_MODE, 11, 1},
{ControlTableItem::SECONDARY_ID, 12, 1},
{ControlTableItem::PROTOCOL_VERSION, 13, 1},
{ControlTableItem::HOMING_OFFSET, 20, 4},
{ControlTableItem::MOVING_THRESHOLD, 24, 4},
{ControlTableItem::TEMPERATURE_LIMIT, 31, 1},
{ControlTableItem::MAX_VOLTAGE_LIMIT, 32, 2},
{ControlTableItem::MIN_VOLTAGE_LIMIT, 34, 2},
{ControlTableItem::PWM_LIMIT, 36, 2},
{ControlTableItem::VELOCITY_LIMIT, 44, 4},
{ControlTableItem::MAX_POSITION_LIMIT, 48, 4},
{ControlTableItem::MIN_POSITION_LIMIT, 52, 4},
{ControlTableItem::SHUTDOWN, 63, 1},

{ControlTableItem::TORQUE_ENABLE, 64, 1},
{ControlTableItem::LED, 65, 1},
{ControlTableItem::STATUS_RETURN_LEVEL, 68, 1},
{ControlTableItem::REGISTERED_INSTRUCTION, 69, 1},
{ControlTableItem::HARDWARE_ERROR_STATUS, 70, 1},
{ControlTableItem::VELOCITY_I_GAIN, 76, 2},
{ControlTableItem::VELOCITY_P_GAIN, 78, 2},
{ControlTableItem::POSITION_D_GAIN, 80, 2},
{ControlTableItem::POSITION_I_GAIN, 82, 2},
{ControlTableItem::POSITION_P_GAIN, 84, 2},
{ControlTableItem::FEEDFORWARD_2ND_GAIN, 88, 2},
{ControlTableItem::FEEDFORWARD_1ST_GAIN, 90, 2},
{ControlTableItem::BUS_WATCHDOG, 98, 2},
{ControlTableItem::GOAL_PWM, 100, 2},
{ControlTableItem::GOAL_VELOCITY, 104, 4},
{ControlTableItem::PROFILE_ACCELERATION, 108, 4},
{ControlTableItem::PROFILE_VELOCITY, 112, 4},
{ControlTableItem::GOAL_POSITION, 116, 4},
{ControlTableItem::REALTIME_TICK, 120, 2},
{ControlTableItem::MOVING, 122, 1},
{ControlTableItem::MOVING_STATUS, 123, 1},
{ControlTableItem::PRESENT_PWM, 124, 2},
{ControlTableItem::PRESENT_VELOCITY, 128, 4},
{ControlTableItem::PRESENT_POSITION, 132, 4},
{ControlTableItem::VELOCITY_TRAJECTORY, 136, 4},
{ControlTableItem::POSITION_TRAJECTORY, 140, 4},
{ControlTableItem::PRESENT_INPUT_VOLTAGE, 144, 2},
{ControlTableItem::PRESENT_TEMPERATURE, 146, 1},
#endif
{ControlTableItem::LAST_DUMMY_ITEM, 0, 0}
};

actuator.hactuator.cpp는 함께 동작하는 헤더 파일소스 파일입니다. 이 두 파일은 서로 협력하여 컨트롤 테이블 항목을 정의하고 데이터를 읽고 쓸 수 있도록 합니다.

  1. actuator.h 파일은 인터페이스 역할을 합니다. 여기서 각 컨트롤 테이블 항목을 정의하는 **enum**을 포함하고, 함수의 선언도 이루어집니다. 예를 들어, 각 항목의 이름을 정의하고, 이 항목을 사용하기 위한 함수들의 선언이 포함됩니다.
  2. actuator.cpp 파일은 구현을 담당합니다. 여기서는 **enum**에서 정의한 항목에 대해 실제 메모리 주소를 설정하고, 해당 항목을 읽고 쓸 수 있도록 하는 구현 코드가 포함됩니다. 즉, actuator.cppactuator.h에서 선언된 함수들을 실제로 어떻게 동작할지 정의합니다.

예시:

  • **actuator.h**에서 PRESENT_VOLTAGE와 같은 컨트롤 테이블 항목을 이름으로 정의합니다.
  • **actuator.cpp**에서 이 항목에 대한 실제 메모리 주소를 설정하고, 이 주소를 통해 데이터를 읽거나 쓸 수 있게 합니다.

따라서 actuator.h사용자에게 제공되는 인터페이스로, 어떤 항목을 사용할 수 있는지를 보여주고, actuator.cpp는 이 항목을 실제 메모리 주소와 연결하여 데이터 처리와 제어가 이루어지도록 합니다.

이해가 안되는데 raw data로 예시들어줄 수 있을까요??

actuator.cpp에서 실제 메모리 주소를 설정하고 이 주소를 통해 데이터를 읽거나 쓸 수 있게 합니다. 라고 되어 있는데 실제 주소가 PRESENT_INPUT_VOLTAGE 144번이락도 되어있는데 이 주소로 안불러집니다. 실제로는 코딩으로 구현했을때 85번으로 불려지구요. 이 이유가 궁금합니다.

불러와서 사용하는것은 헤더파일의 enum ControlTableItemIndex 값이니 그런것입니다. 주소를 불러 직관적으로 사용하시려면 예제 "read_x"를 참고해주세요.