0편 – [robot arm, manipulator 프로젝트][ubuntu 20.04, ros2, gazebo][0] – ros2, gazebo 설치 및 예제 실행
1편 – [robot arm, manipulator 프로젝트][ubuntu 20.04, ros2, gazebo][1] – ros2 publisher subscriber c++, python 구현 및 실행
2편 – [robot arm, manipulator 프로젝트][ubuntu 20.04, ros2, gazebo][2] – ros2 와 아두이노 통신 (micro ros arduino), OpenCR, robotis, dynamixel 모터 개발 환경 구성 및 예제
3편 – [robot arm, manipulator 프로젝트][ubuntu 20.04, ros2, gazebo][3] – ros2 와 아두이노(OpenCR)로 여러개의 dynamixel 모터 제어 및 엔코더 값 받기, robotis

computer 에서 OpenCR 을 거쳐서 모터를 제어하려고 하였지만 OpenCR 의 부하가 커질 뿐 아니라 로보티즈에서도 로봇을 만들 때 u2d2 를 통해 모터를 제어하고 있는 것으로 확인하여 시스템 구성도를 수정하여 진행

따라서, 2편, 3편은 생략하여도 된다.

1.

mkdir ros2_ws && cd ros2_ws
mkdir src && cd src
git clone -b ros2 https://github.com/ROBOTIS-GIT/DynamixelSDK.git 
git clone -b ros2 https://github.com/ROBOTIS-GIT/dynamixel-workbench.git
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake test_dynamixel
cd ~/ros2_ws
colcon build

이후 computer-u2d2-dynamixel motor 를 연결한 뒤에

ls /dev/tty*
sudo usermod -aG dialout <username>
image

test_dynamixel/src 폴더에 test_move_vel.cpp, test_move_pos.cpp 파일 추가

#include <rclcpp/rclcpp.hpp>
#include "std_msgs/msg/int16.hpp"
#include "dynamixel_sdk/dynamixel_sdk.h"

rclcpp::Node::SharedPtr node = nullptr;
using namespace dynamixel;

// Control table address
#define ADDR_OPERATING_MODE   11              // 제어모드
#define ADDR_TORQUE_ENABLE    64              // Torque on/off 제어
#define ADDR_GOAL_POSITION    116             // 위치 제어
#define ADDR_PRESENT_POSITION 132             // 현재 위치

// Protocol version
#define PROTOCOL_VERSION      2.0             // Default Protocol version of DYNAMIXEL X series.

// Default setting
#define DXL1_ID               13              // DXL1 ID
#define DXL2_ID               14              // DXL2 ID
#define BAUDRATE              57600           // Default Baudrate of DYNAMIXEL X series
#define DEVICE_NAME           "/dev/ttyUSB0"  // Port name

#define VALUE                 2048            // 초기 목표 위치 (중간값)
#define OPERATING_VALUE       3               // 제어모드, 위치제어: 3

PortHandler *portHandler;
PacketHandler *packetHandler;

void topic_callback(const std_msgs::msg::Int16::SharedPtr msg)
{
  uint8_t dxl_error = 0;

  // 목표 위치 설정 (msg->data를 클램핑하여 사용)
  int target_position = msg->data;
  if (target_position < 0) target_position = 0;
  if (target_position > 4095) target_position = 4095;

  // 모터 위치 설정
  packetHandler->write4ByteTxRx(portHandler, DXL1_ID, ADDR_GOAL_POSITION, target_position, &dxl_error);
  RCLCPP_INFO(node->get_logger(), "Set Position: %d", target_position);
}

int main(int argc, char **argv)
{
  uint8_t dxl_error = 0;

  rclcpp::init(argc, argv);
  node = rclcpp::Node::make_shared("Dynamixel");

  // PortHandler와 PacketHandler 초기화
  portHandler = dynamixel::PortHandler::getPortHandler(DEVICE_NAME);
  packetHandler = dynamixel::PacketHandler::getPacketHandler(PROTOCOL_VERSION);

  // 포트 열기
  if (!portHandler->openPort())
  {
    RCLCPP_ERROR(node->get_logger(), "Failed to open the port!");
    return -1;
  }

  // Baudrate 설정
  if (!portHandler->setBaudRate(BAUDRATE))
  {
    RCLCPP_ERROR(node->get_logger(), "Failed to set the baudrate!");
    return -1;
  }

  // 제어모드를 위치 제어로 설정
  packetHandler->write1ByteTxRx(portHandler, DXL1_ID, ADDR_OPERATING_MODE, OPERATING_VALUE, &dxl_error);
  if (dxl_error != 0)
  {
    RCLCPP_ERROR(node->get_logger(), "Failed to set operating mode!");
    return -1;
  }

  // Torque Enable
  packetHandler->write1ByteTxRx(portHandler, DXL1_ID, ADDR_TORQUE_ENABLE, 1, &dxl_error);
  if (dxl_error != 0)
  {
    RCLCPP_ERROR(node->get_logger(), "Failed to enable torque!");
    return -1;
  }

  RCLCPP_INFO(node->get_logger(), "Dynamixel initialized in position control mode.");

  // 초기 목표 위치 설정
  packetHandler->write4ByteTxRx(portHandler, DXL1_ID, ADDR_GOAL_POSITION, VALUE, &dxl_error);

  // ROS2 구독자 생성
  auto subscription = node->create_subscription<std_msgs::msg::Int16>(
    "/cmd_position", 10, topic_callback);

  // ROS2 스핀
  rclcpp::spin(node);

  // 종료 시 Torque Disable
  packetHandler->write1ByteTxRx(portHandler, DXL1_ID, ADDR_TORQUE_ENABLE, 0, &dxl_error);

  rclcpp::shutdown();
  portHandler->closePort();

  return 0;
}

위 코드가 test_move_pos.cpp

#include <rclcpp/rclcpp.hpp>
#include "std_msgs/msg/int16.hpp"
#include "dynamixel_sdk/dynamixel_sdk.h"

rclcpp::Node::SharedPtr node = nullptr;
using namespace dynamixel;

// Control table address
#define ADDR_OPERTAING_MODE   11
#define ADDR_TORQUE_ENABLE    64              // Torque on/off 제어
#define ADDR_GOAL_VELOCITY    104             // 속도 제어
#define ADDR_GOAL_POSITION    116             // 위치 제어
#define ADDR_PRESENT_POSITION 132             // 현재 위치



// Protocol version
#define PROTOCOL_VERSION      2.0             // Default Protocol version of DYNAMIXEL X series.

// Default setting
#define DXL1_ID               13               // DXL1 ID
#define DXL2_ID               14               // DXL2 ID
#define BAUDRATE              57600           // Default Baudrate of DYNAMIXEL X series
#define DEVICE_NAME           "/dev/ttyUSB0"  

#define VALUE                 100
#define OPERATING_VALUE       1               // 제어모드, 위치제어: 3, 속도제어: 1

PortHandler *portHandler;
PacketHandler *packetHandler;

void topic_callback(const std_msgs::msg::Int16::SharedPtr msg)
{
  uint8_t dxl_error = 0;
  packetHandler->write4ByteTxRx(portHandler, DXL1_ID, ADDR_GOAL_VELOCITY, msg->data,&dxl_error);
  RCLCPP_INFO(node->get_logger(), "velocity: %d", msg->data);
}


int main(int argc, char **argv)
{
  uint8_t dxl_error = 0;
  rclcpp::init(argc, argv);
  node = rclcpp::Node::make_shared("Dynamixel");

  portHandler = dynamixel::PortHandler::getPortHandler(DEVICE_NAME);
  packetHandler = dynamixel::PacketHandler::getPacketHandler(PROTOCOL_VERSION);

  if(portHandler->openPort() == false)
  {
    RCLCPP_INFO(node->get_logger(),"Failed to open the port!");
  }
  if(portHandler->setBaudRate(BAUDRATE) == false)
  {
    RCLCPP_INFO(node->get_logger(),"Failed to set the baudrate!");
  }

  packetHandler->write1ByteTxRx(portHandler, DXL1_ID, ADDR_OPERTAING_MODE, OPERATING_VALUE, &dxl_error);
  packetHandler->write1ByteTxRx(portHandler, DXL1_ID, ADDR_TORQUE_ENABLE, 1, &dxl_error);
  RCLCPP_INFO(node->get_logger(),"start setting");
  packetHandler->write4ByteTxRx(portHandler, DXL1_ID, ADDR_GOAL_VELOCITY, VALUE);

  auto subscription =
    node->create_subscription<std_msgs::msg::Int16>("/cmd_velocty", 10, topic_callback);

  rclcpp::spin(node);
  packetHandler->write1ByteTxRx(portHandler, DXL1_ID, ADDR_TORQUE_ENABLE, 0, &dxl_error);
  rclcpp::shutdown();
  
 return 0;
}

위 코드가 test_move_vel.cpp

test_dynamixel/src 폴더에 있는 CMakeLists.txt 파일 수정

cmake_minimum_required(VERSION 3.5)
project(test_dynamixel)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rclcpp REQUIRED)
find_package(dynamixel_sdk REQUIRED)
find_package(dynamixel_workbench_toolbox REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

# add_executable(test_move_vel src/test_move_vel.cpp)
# ament_target_dependencies(test_move_vel rclcpp dynamixel_sdk dynamixel_workbench_toolbox std_msgs)

# add_executable(test_move_pos src/test_move_pos.cpp)
# ament_target_dependencies(test_move_pos rclcpp dynamixel_sdk dynamixel_workbench_toolbox std_msgs)

# install(TARGETS
# test_move_pos
# test_move_vel
#   DESTINATION lib/${PROJECT_NAME})


set(EXECUTABLES
  test_move_vel
  test_move_pos
)

foreach(EXEC IN LISTS EXECUTABLES)
  add_executable(${EXEC} src/${EXEC}.cpp)
  ament_target_dependencies(${EXEC} rclcpp dynamixel_sdk dynamixel_workbench_toolbox std_msgs)
  install(TARGETS ${EXEC}
    DESTINATION lib/${PROJECT_NAME})
endforeach()
  
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

test_dynamixel/src 폴더에 있는 package.xml 파일 수정

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>test_dynamixel</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="jw@todo.todo">jw</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>
  <depend>rclcpp</depend>
  <depend>dynamixel_sdk</depend>
  <depend>dynamixel_workbench_toolbox</depend>
  <depend>std_msgs</depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

이제

cd ~/ros2_ws/
colcon build
source /opt/ros/foxy/setup.bash
source ~/ros2_ws/install/local_setup.bash
ros2 run test_dynamixel test_move_pos /dev/ttyUSB0

혹은

cd ~/ros2_ws/
colcon build
source /opt/ros/foxy/setup.bash
source ~/ros2_ws/install/local_setup.bash
ros2 run test_dynamixel test_move_vel /dev/ttyUSB0
image 1

위 사진과 같이 rqt 를 통해 모터의 속도와 위치를 변화시켜 볼 수 있다.


0 Comments

Leave a Reply