Trinamic Stepper Motor control
==============================
Introduction
------------
This tutorial is a simple example of how to use the ros2_canopen package to control a `Trinamic smart stepper motor `_.
Getting started
---------------
If you haven't already done so, follow the steps in the :doc:`../user-guide/configuration`.
Configuration
-------------
- Create a new folder in the ``config`` folder of your configuration package. Name it ``single-pd42``.
- Download ``.eds`` file from `Trinamic `_ and place ``TMCM-1270.eds`` in the ``single-pd42`` folder.
- Create a ``bus.yml`` file in the ``single-pd42`` folder with the following content:
.. code-block:: yaml
options:
dcf_path: "@BUS_CONFIG_PATH@"
master:
node_id: 2
driver: "ros2_canopen::MasterDriver"
package: "canopen_master_driver"
sync_period: 20000
defaults:
dcf: "TMCM-1270.eds"
driver: "ros2_canopen::Cia402Driver"
package: "canopen_402_driver"
polling: false
heartbeat_producer: 1000 # Heartbeat every 1000 ms
sdo: # SDO executed during config
- {index: 0x6081, sub_index: 0, value: 500000} # Set velocity
- {index: 0x6083, sub_index: 0, value: 1000000} # Set acceleration
- {index: 0x6083, sub_index: 0, value: 1000000} # Set deceleration
- {index: 0x6085, sub_index: 0, value: 1000000} # Set quickstop deceleration
- {index: 0x6098, sub_index: 0, value: 35} # Set default homing mode to 35
- {index: 0x60C2, sub_index: 1, value: 50} # Set interpolation time for cyclic modes to 50 ms
- {index: 0x60C2, sub_index: 2, value: -3} # Set base 10-3s
nodes:
trinamic_pd42:
node_id: 1
- Edit the ``CMakeLists.txt`` file in the ``config`` folder of your configuration package and add the following lines:
.. code-block:: cmake
cmake_minimum_required(VERSION 3.8)
project(trinamic_pd42_can)
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(rclcpp REQUIRED)
find_package(std_srvs REQUIRED)
find_package(canopen REQUIRED)
find_package(lely_core_libraries REQUIRED)
find_package(canopen_interfaces REQUIRED)
# generate_dcf(single-pd42)
cogen_dcf(single-pd42)
add_executable(position_tick_client src/position_tick_motor.cpp)
ament_target_dependencies(position_tick_client
rclcpp std_srvs canopen_interfaces)
install(TARGETS
position_tick_client
DESTINATION lib/${PROJECT_NAME})
# install launch file
install(DIRECTORY
launch/
DESTINATION share/${PROJECT_NAME}
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
endif()
ament_package()
- Create launch file in folder ``launch`` and add the following content:
.. code-block:: python
import os
import sys
import launch
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python import get_package_share_directory
from launch import LaunchDescription
def generate_launch_description():
ld = LaunchDescription()
slave_eds_path = os.path.join(
get_package_share_directory("trinamic_pd42_can"), "config", "single-pd42", "TMCM-1270.eds"
)
slave_node_1 = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[
os.path.join(get_package_share_directory("canopen_fake_slaves"), "launch"),
"/cia402_slave.launch.py",
]
),
launch_arguments={
"node_id": "1",
"node_name": "pd42_slave",
"slave_config": slave_eds_path,
}.items(),
)
master_bin_path = os.path.join(
get_package_share_directory("trinamic_pd42_can"),
"config",
"single-pd42",
"master.bin",
)
if not os.path.exists(master_bin_path):
master_bin_path = ""
device_container = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[
os.path.join(get_package_share_directory("canopen_core"), "launch"),
"/canopen.launch.py",
]
),
launch_arguments={
"master_config": os.path.join(
get_package_share_directory("trinamic_pd42_can"),
"config",
"single-pd42",
"master.dcf",
),
"master_bin": master_bin_path,
"bus_config": os.path.join(
get_package_share_directory("trinamic_pd42_can"),
"config",
"single-pd42",
"bus.yml",
),
"can_interface_name": "vcan0",
}.items(),
)
ld.add_action(device_container)
ld.add_action(slave_node_1)
return ld
Running the example
-------------------
To begin, follow the instructions for :doc:`../quickstart/operation`, which can be done using either a virtual or peak CAN interface.
If you prefer to use a real CAN interface, you will need to modify the launch file by changing the ``can_interface_name`` argument to ``can0``.
Additionally, if you are using real hardware, you should comment out the fake slave launch by adding a *#* in front of the line *ld.add_action(slave_node_1)*.
Once these changes have been made, you can launch the example.
.. code-block:: console
ros2 launch trinamic_pd42_can .launch.py
Initilaize the motor by calling the service ``/trinamic_pd42/init``:
.. code-block:: console
ros2 service call /trinamic_pd42/init std_srvs/srv/Trigger
Set the operation mode to ``Profile Position Mode`` by calling the service ``/trinamic_pd42/position_mode``:
.. code-block:: console
ros2 service call /trinamic_pd42/position_mode std_srvs/srv/Trigger
Set the target to the motor by calling the service ``/trinamic_pd42/target``:
.. code-block:: console
ros2 service call /trinamic_pd42/target canopen_interfaces/srv/COTargetDouble "{ target: 10.0 }"
Reference
---------
You can find the source code for this example in the `trinamic_pd42_can `_ package.