@@ -32,6 +32,8 @@ ament_target_dependencies( | |||||
rclcpp_lifecycle | rclcpp_lifecycle | ||||
) | ) | ||||
target_link_libraries(hexapod_robot PUBLIC i2c) | |||||
pluginlib_export_plugin_description_file(hardware_interface hexapod_robot.xml) | pluginlib_export_plugin_description_file(hardware_interface hexapod_robot.xml) | ||||
@@ -75,7 +77,9 @@ ament_export_dependencies( | |||||
hardware_interface | hardware_interface | ||||
pluginlib | pluginlib | ||||
rclcpp | rclcpp | ||||
rclcpp_lifecycle | |||||
rcl | |||||
cpp_lifecycle | |||||
i2c | |||||
) | ) | ||||
@@ -1,32 +1,58 @@ | |||||
controller_manager: | controller_manager: | ||||
ros__parameters: | ros__parameters: | ||||
update_rate: 10 | |||||
update_rate: 50 | |||||
joint_state_broadcaster: | joint_state_broadcaster: | ||||
type: joint_state_broadcaster/JointStateBroadcaster | type: joint_state_broadcaster/JointStateBroadcaster | ||||
joint_trajectory_controller: | |||||
type: joint_trajectory_controller/JointTrajectoryController | |||||
forward_position_controller: | forward_position_controller: | ||||
type: forward_command_controller/ForwardCommandController | type: forward_command_controller/ForwardCommandController | ||||
forward_position_controller: | forward_position_controller: | ||||
ros__parameters: | |||||
joints: | |||||
- t3_joint | |||||
interface_name: position | |||||
joint_trajectory_controller: | |||||
ros__parameters: | ros__parameters: | ||||
joints: | joints: | ||||
- c1_joint | - c1_joint | ||||
- c2_joint | |||||
- c3_joint | |||||
- c4_joint | |||||
- c5_joint | |||||
- c6_joint | |||||
- f1_joint | - f1_joint | ||||
- f2_joint | |||||
- f3_joint | |||||
- f4_joint | |||||
- f5_joint | |||||
- f6_joint | |||||
- t1_joint | - t1_joint | ||||
- c2_joint | |||||
- f2_joint | |||||
- t2_joint | - t2_joint | ||||
- c3_joint | |||||
- f3_joint | |||||
- t3_joint | - t3_joint | ||||
- c4_joint | |||||
- f4_joint | |||||
- t4_joint | - t4_joint | ||||
- c5_joint | |||||
- f5_joint | |||||
- t5_joint | - t5_joint | ||||
- c6_joint | |||||
- f6_joint | |||||
- t6_joint | - t6_joint | ||||
interface_name: position | |||||
command_interfaces: | |||||
- position | |||||
state_interfaces: | |||||
- position | |||||
action_monitor_rate: 20.0 | |||||
allow_partial_joints_goal: false | |||||
@@ -25,13 +25,6 @@ def generate_launch_description(): | |||||
robot_description = {"robot_description": robot_description_content} | robot_description = {"robot_description": robot_description_content} | ||||
robot_controllers = PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "controllers.yaml"]) | robot_controllers = PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "controllers.yaml"]) | ||||
control_node = Node( | |||||
package = "controller_manager", | |||||
executable = "ros2_control_node", | |||||
parameters = [robot_description, robot_controllers], | |||||
output="both" | |||||
) | |||||
robot_state_publisher_node = Node( | robot_state_publisher_node = Node( | ||||
package = "robot_state_publisher", | package = "robot_state_publisher", | ||||
@@ -40,51 +33,69 @@ def generate_launch_description(): | |||||
output = "both" | output = "both" | ||||
) | ) | ||||
joint_state_publisher_gui = Node( | |||||
package="joint_state_publisher_gui", | |||||
executable="joint_state_publisher_gui" | |||||
) | |||||
rviz_node = Node( | |||||
package = "rviz2", | |||||
executable = "rviz2", | |||||
arguments = ['-d', PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "hexapod_robot.rviz"])], | |||||
name = "rviz2", | |||||
output = "screen", | |||||
control_node = Node( | |||||
package = "controller_manager", | |||||
executable = "ros2_control_node", | |||||
parameters = [robot_description, robot_controllers], | |||||
output="both" | |||||
) | ) | ||||
# rviz_node = Node( | |||||
# package = "rviz2", | |||||
# executable = "rviz2", | |||||
# arguments = ['-d', PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "hexapod_robot.rviz"])], | |||||
# name = "rviz2", | |||||
# output = "screen", | |||||
# ) | |||||
joint_state_broadcaster_spawner = Node ( | joint_state_broadcaster_spawner = Node ( | ||||
package = "controller_manager", | package = "controller_manager", | ||||
executable = "spawner", | executable = "spawner", | ||||
arguments = ["joint_state_broadcaster", "--controller-manager", "/controller_manager"] | arguments = ["joint_state_broadcaster", "--controller-manager", "/controller_manager"] | ||||
) | ) | ||||
robot_controller_spawner = Node( | |||||
# forward_position_controller_spawner = Node( | |||||
# package = "controller_manager", | |||||
# executable = "spawner", | |||||
# arguments = ["forward_position_controller", "--controller-manager", "/controller_manager"] | |||||
# ) | |||||
joint_trajectory_controller_spawner = Node( | |||||
package = "controller_manager", | package = "controller_manager", | ||||
executable = "spawner", | executable = "spawner", | ||||
arguments = ["forward_position_controller", "--controller-manager", "/controller_manager"] | |||||
) | |||||
delay_rviz_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
event_handler = OnProcessExit( | |||||
target_action = joint_state_broadcaster_spawner, | |||||
on_exit = [rviz_node] | |||||
) | |||||
arguments = ["joint_trajectory_controller", "--controller-manager", "/controller_manager"] | |||||
) | ) | ||||
# delay_rviz_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
# event_handler = OnProcessExit( | |||||
# target_action = joint_state_broadcaster_spawner, | |||||
# on_exit = [rviz_node] | |||||
# ) | |||||
# ) | |||||
delay_robot_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
# delay_forward_position_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
# event_handler = OnProcessExit( | |||||
# target_action = joint_state_broadcaster_spawner, | |||||
# on_exit = [forward_position_controller_spawner] | |||||
# ) | |||||
# ) | |||||
delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
event_handler = OnProcessExit( | event_handler = OnProcessExit( | ||||
target_action = joint_state_broadcaster_spawner, | target_action = joint_state_broadcaster_spawner, | ||||
on_exit = [robot_controller_spawner] | |||||
on_exit = [joint_trajectory_controller_spawner] | |||||
) | ) | ||||
) | ) | ||||
nodes = [ | nodes = [ | ||||
control_node, | control_node, | ||||
robot_state_publisher_node, | robot_state_publisher_node, | ||||
joint_state_broadcaster_spawner, | joint_state_broadcaster_spawner, | ||||
delay_rviz_after_joint_state_broadcaster_spawner, | |||||
delay_robot_controller_spawner_after_joint_state_broadcaster_spawner | |||||
#delay_rviz_after_joint_state_broadcaster_spawner, | |||||
# delay_forward_position_controller_spawner_after_joint_state_broadcaster_spawner, | |||||
delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner | |||||
] | ] | ||||
return LaunchDescription(nodes) | return LaunchDescription(nodes) |
@@ -55,11 +55,12 @@ | |||||
<material name="blue"/> | <material name="blue"/> | ||||
</visual> | </visual> | ||||
</link> | </link> | ||||
<joint name="${name}_joint" type="continuous"> | |||||
<joint name="${name}_joint" type="revolute"> | |||||
<parent link="base_link"/> | <parent link="base_link"/> | ||||
<child link="${name}_link"/> | <child link="${name}_link"/> | ||||
<origin xyz="${xy_offset} 0"/> | <origin xyz="${xy_offset} 0"/> | ||||
<axis xyz="0 0 1"/> | <axis xyz="0 0 1"/> | ||||
<limit effort="30" velocity="100" lower="-1" upper="1"/> | |||||
</joint> | </joint> | ||||
</xacro:macro> | </xacro:macro> | ||||
@@ -84,12 +85,13 @@ | |||||
</visual> | </visual> | ||||
--> | --> | ||||
</link> | </link> | ||||
<joint name="${name}_joint" type="continuous"> | |||||
<joint name="${name}_joint" type="revolute"> | |||||
<parent link="${parent_name}_link"/> | <parent link="${parent_name}_link"/> | ||||
<child link="${name}_link"/> | <child link="${name}_link"/> | ||||
<xacro:if value="${side == 'l'}"><origin xyz="-0.020 0.03 -0.0075"/></xacro:if> | <xacro:if value="${side == 'l'}"><origin xyz="-0.020 0.03 -0.0075"/></xacro:if> | ||||
<xacro:if value="${side == 'r'}"><origin xyz=" 0.020 0.03 -0.0075"/></xacro:if> | <xacro:if value="${side == 'r'}"><origin xyz=" 0.020 0.03 -0.0075"/></xacro:if> | ||||
<axis xyz="0 1 0"/> | <axis xyz="0 1 0"/> | ||||
<limit effort="30" velocity="100" lower="-1" upper="1"/> | |||||
</joint> | </joint> | ||||
</xacro:macro> | </xacro:macro> | ||||
@@ -141,12 +143,13 @@ | |||||
</visual> | </visual> | ||||
</link> | </link> | ||||
<joint name="${name}_joint" type="continuous"> | |||||
<joint name="${name}_joint" type="revolute"> | |||||
<parent link="${parent_name}_link"/> | <parent link="${parent_name}_link"/> | ||||
<child link="${name}_link"/> | <child link="${name}_link"/> | ||||
<xacro:if value="${side == 'l'}"><origin xyz="-0.080 -0.03 0"/></xacro:if> | <xacro:if value="${side == 'l'}"><origin xyz="-0.080 -0.03 0"/></xacro:if> | ||||
<xacro:if value="${side == 'r'}"><origin xyz=" 0.080 -0.03 0"/></xacro:if> | <xacro:if value="${side == 'r'}"><origin xyz=" 0.080 -0.03 0"/></xacro:if> | ||||
<axis xyz="0 1 0"/> | <axis xyz="0 1 0"/> | ||||
<limit effort="30" velocity="100" lower="-1" upper="1"/> | |||||
</joint> | </joint> | ||||
</xacro:macro> | </xacro:macro> | ||||
@@ -27,44 +27,49 @@ | |||||
</joint> | </joint> | ||||
</xacro:macro> | </xacro:macro> | ||||
<xacro:macro name="map_joint_name_to_hardware" params="name i2c_address channel"> | |||||
<xacro:macro name="map_joint_name_to_hardware" params="name i2c_address channel counts_intersect counts_slope"> | |||||
<param name="${name}_joint_i2c_address">${i2c_address}</param> | <param name="${name}_joint_i2c_address">${i2c_address}</param> | ||||
<param name="${name}_joint_channel">${channel}</param> | <param name="${name}_joint_channel">${channel}</param> | ||||
<param name="${name}_joint_counts_intersect">${counts_intersect}</param> | |||||
<param name="${name}_joint_counts_slope">${counts_slope}</param> | |||||
</xacro:macro> | </xacro:macro> | ||||
<ros2_control name="Hexapod" type="system"> | <ros2_control name="Hexapod" type="system"> | ||||
<hardware> | <hardware> | ||||
<plugin>hexapod_robot/HexapodRobotHardwareInterface</plugin> | <plugin>hexapod_robot/HexapodRobotHardwareInterface</plugin> | ||||
<param name="i2c_device">/dev/i2c-1</param> | <param name="i2c_device">/dev/i2c-1</param> | ||||
<xacro:map_joint_name_to_hardware name="c1" i2c_address="0x40" channel="13"/> | |||||
<xacro:map_joint_name_to_hardware name="f1" i2c_address="0x40" channel="14"/> | |||||
<xacro:map_joint_name_to_hardware name="t1" i2c_address="0x40" channel="15"/> | |||||
<xacro:map_joint_name_to_hardware name="c1" i2c_address="0x40" channel="13" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="f1" i2c_address="0x40" channel="14" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="t1" i2c_address="0x40" channel="15" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="c2" i2c_address="0x40" channel="9"/> | |||||
<xacro:map_joint_name_to_hardware name="f2" i2c_address="0x40" channel="10"/> | |||||
<xacro:map_joint_name_to_hardware name="t2" i2c_address="0x40" channel="11"/> | |||||
<xacro:map_joint_name_to_hardware name="c2" i2c_address="0x40" channel="9" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="f2" i2c_address="0x40" channel="10" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="t2" i2c_address="0x40" channel="11" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="c3" i2c_address="0x40" channel="2"/> | |||||
<xacro:map_joint_name_to_hardware name="f3" i2c_address="0x40" channel="1"/> | |||||
<xacro:map_joint_name_to_hardware name="t3" i2c_address="0x40" channel="0"/> | |||||
<xacro:map_joint_name_to_hardware name="c3" i2c_address="0x40" channel="2" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="f3" i2c_address="0x40" channel="1" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="t3" i2c_address="0x40" channel="0" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="c4" i2c_address="0x41" channel="13"/> | |||||
<xacro:map_joint_name_to_hardware name="f4" i2c_address="0x41" channel="14"/> | |||||
<xacro:map_joint_name_to_hardware name="t4" i2c_address="0x41" channel="15"/> | |||||
<xacro:map_joint_name_to_hardware name="c4" i2c_address="0x41" channel="13" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="f4" i2c_address="0x41" channel="14" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="t4" i2c_address="0x41" channel="15" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="c5" i2c_address="0x41" channel="6"/> | |||||
<xacro:map_joint_name_to_hardware name="f5" i2c_address="0x41" channel="5"/> | |||||
<xacro:map_joint_name_to_hardware name="t5" i2c_address="0x41" channel="4"/> | |||||
<xacro:map_joint_name_to_hardware name="c5" i2c_address="0x41" channel="6" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="f5" i2c_address="0x41" channel="5" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="t5" i2c_address="0x41" channel="4" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="c6" i2c_address="0x41" channel="2"/> | |||||
<xacro:map_joint_name_to_hardware name="f6" i2c_address="0x41" channel="1"/> | |||||
<xacro:map_joint_name_to_hardware name="t6" i2c_address="0x41" channel="0"/> | |||||
<xacro:map_joint_name_to_hardware name="c6" i2c_address="0x41" channel="2" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="f6" i2c_address="0x41" channel="1" counts_intersect="307" counts_slope="129"/> | |||||
<xacro:map_joint_name_to_hardware name="t6" i2c_address="0x41" channel="0" counts_intersect="307" counts_slope="129"/> | |||||
</hardware> | </hardware> | ||||
<xacro:create_leg_joints number="1"/> | <xacro:create_leg_joints number="1"/> | ||||
<xacro:create_leg_joints number="2"/> | <xacro:create_leg_joints number="2"/> | ||||
<xacro:create_leg_joints number="3"/> | <xacro:create_leg_joints number="3"/> | ||||
<xacro:create_leg_joints number="4"/> | <xacro:create_leg_joints number="4"/> | ||||
<xacro:create_leg_joints number="5"/> | <xacro:create_leg_joints number="5"/> | ||||
<xacro:create_leg_joints number="6"/> | <xacro:create_leg_joints number="6"/> | ||||
</ros2_control> | </ros2_control> | ||||
</robot> | </robot> |
@@ -4,6 +4,18 @@ | |||||
#include "rclcpp/rclcpp.hpp" | #include "rclcpp/rclcpp.hpp" | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <unistd.h> | |||||
#include <stdlib.h> | |||||
#include <stdio.h> | |||||
#include <fcntl.h> | |||||
#include <stdint.h> | |||||
#include <sys/ioctl.h> | |||||
#include <linux/i2c-dev.h> | |||||
extern "C" { | |||||
#include <i2c/smbus.h> | |||||
} | |||||
namespace hexapod_robot { | namespace hexapod_robot { | ||||
@@ -12,7 +24,7 @@ HexapodRobotHardwareInterface::on_init(const hardware_interface::HardwareInfo &i | |||||
if (hardware_interface::SystemInterface::on_init(info) != hardware_interface::CallbackReturn::SUCCESS) { | if (hardware_interface::SystemInterface::on_init(info) != hardware_interface::CallbackReturn::SUCCESS) { | ||||
return hardware_interface::CallbackReturn::ERROR; | return hardware_interface::CallbackReturn::ERROR; | ||||
} | } | ||||
RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "on_init"); | RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "on_init"); | ||||
for (const hardware_interface::ComponentInfo &joint: info_.joints) { | for (const hardware_interface::ComponentInfo &joint: info_.joints) { | ||||
@@ -21,30 +33,106 @@ HexapodRobotHardwareInterface::on_init(const hardware_interface::HardwareInfo &i | |||||
std::string i2c_address_parameter = joint.name + "_i2c_address"; | std::string i2c_address_parameter = joint.name + "_i2c_address"; | ||||
std::string channel_parameter = joint.name + "_channel"; | std::string channel_parameter = joint.name + "_channel"; | ||||
std::string counts_intersect_parameter = joint.name + "_counts_intersect"; | |||||
std::string counts_slope_parameter = joint.name + "_counts_slope"; | |||||
PWMOutput pwm_output; | PWMOutput pwm_output; | ||||
pwm_output.i2c_address = stoi(info_.hardware_parameters[i2c_address_parameter], nullptr, 0); | |||||
pwm_output.channel = stoi(info_.hardware_parameters[channel_parameter], nullptr, 0); | |||||
pwm_output.i2c_address = stoi(info_.hardware_parameters[i2c_address_parameter], nullptr, 0); | |||||
pwm_output.channel = stoi(info_.hardware_parameters[channel_parameter], nullptr, 0); | |||||
pwm_output.counts_intersect = stod(info_.hardware_parameters[counts_intersect_parameter]); | |||||
pwm_output.counts_slope = stod(info_.hardware_parameters[counts_slope_parameter]); | |||||
pca9685_controller_available_[pwm_output.i2c_address] = false; | |||||
pca_available_[pwm_output.i2c_address] = false; | |||||
joint_pwm_mapping_[joint.name] = pwm_output; | joint_pwm_mapping_[joint.name] = pwm_output; | ||||
RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "%s mapped to 0x%02x:%d", joint.name.c_str(), pwm_output.i2c_address, pwm_output.channel); | |||||
RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "%s mapped to 0x%02x:%d. intersect: %.4f slope: %.4f", joint.name.c_str(), pwm_output.i2c_address, pwm_output.channel, pwm_output.counts_intersect, pwm_output.counts_slope); | |||||
} | } | ||||
for (const auto& [i2c_address, available]: pca9685_controller_available_) { | |||||
RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "require PCA9685 at i2c 0x%02x.", i2c_address); | |||||
} | |||||
i2c_available_ = false; | |||||
i2c_fd_ = open(info_.hardware_parameters["i2c_device"].c_str(), O_RDWR); | |||||
i2c_available_ = i2c_open_(info_.hardware_parameters["i2c_device"]); | |||||
pca_initialize_(); | |||||
return hardware_interface::CallbackReturn::SUCCESS; | |||||
} | |||||
bool | |||||
HexapodRobotHardwareInterface::i2c_open_(const std::string& device) { | |||||
i2c_fd_ = open(device.c_str(), O_RDWR); | |||||
if (i2c_fd_ < 0) { | if (i2c_fd_ < 0) { | ||||
RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "Cannot open %s, continuing in dummy mode.", info_.hardware_parameters["i2c_device"].c_str()); | RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "Cannot open %s, continuing in dummy mode.", info_.hardware_parameters["i2c_device"].c_str()); | ||||
} else { | |||||
i2c_available_ = true; | |||||
return false; | |||||
} | } | ||||
return true; | |||||
} | |||||
return hardware_interface::CallbackReturn::SUCCESS; | |||||
void | |||||
HexapodRobotHardwareInterface::pca_initialize_() { | |||||
for (const auto& [i2c_address, available]: pca_available_) { | |||||
ioctl(i2c_fd_, I2C_SLAVE, i2c_address); | |||||
if(i2c_smbus_write_byte_data(i2c_fd_, pca_reg_mode1_, 0x00) < 0) { | |||||
RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "Could not open i2c device %02x, marking as unavailable.", i2c_address); | |||||
pca_available_[i2c_address] = false; | |||||
} else { | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_mode1_, pca_mode1_sleep_); | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_mode2_, pca_mode2_outdrv_); | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_prescale_, (uint8_t)pca_prescaler_value_); | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_mode1_, pca_mode1_restart_); | |||||
RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "PCA9685 at i2c 0x%02x initialized.", i2c_address); | |||||
pca_available_[i2c_address] = true; | |||||
} | |||||
} | |||||
} | |||||
bool | |||||
HexapodRobotHardwareInterface::pca_set_pwm_(uint8_t i2c_address, uint8_t channel_number, uint16_t counts) { | |||||
// if(i2c_address == 0x40 && channel_number == 0) { | |||||
// RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "pca_set_pwm(%02x, %02x, %04x)", i2c_address, channel_number, counts); | |||||
// } | |||||
if(channel_number>15) { | |||||
RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "pca_set_pwm: channel %02x >15", channel_number); | |||||
return false; | |||||
} | |||||
if(counts > 0x1000) { | |||||
RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "pca_set_pwm: counts %04x >15", counts); | |||||
return false; | |||||
} | |||||
if(!pca_available_[i2c_address]) { | |||||
// RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "pca_set_pwm: i2c_address %02x unavailable.", i2c_address); | |||||
return false; | |||||
} | |||||
if(ioctl(i2c_fd_, I2C_SLAVE, i2c_address) < 0) { | |||||
RCLCPP_WARN(rclcpp::get_logger("HexapodRobotHardwareInterface"), "pca_set_pwm: ioctl failed."); | |||||
return false; | |||||
} | |||||
// adding channel_number * 4 to the base address for LED0 output given by the constants | |||||
// yields the addresses of all PWM channels | |||||
if(counts == 0) { | |||||
// output always off | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_on_l_ + channel_number * 4, 0x00); // ON_L | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_on_h_ + channel_number * 4, 0x00); // ON_H | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_off_l_ + channel_number * 4, 0x00); // OFF_L | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_off_h_ + channel_number * 4, 0x10); // OFF_H == 0x10 => LED full OFF | |||||
} else if(counts == 0x1000) { | |||||
// output always on | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_on_l_ + channel_number * 4, 0x00); // ON_L | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_on_h_ + channel_number * 4, 0x10); // ON_H == 0x10 => LED full ON | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_off_l_ + channel_number * 4, 0x00); // OFF_L | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_off_h_ + channel_number * 4, 0x00); // OFF_H | |||||
} else { | |||||
// duty cycle from 0 to counts | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_on_h_ + channel_number * 4, 0x00); | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_on_l_ + channel_number * 4, 0x00); | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_off_l_ + channel_number * 4, counts & 0xff); // lower byte | |||||
i2c_smbus_write_byte_data(i2c_fd_, pca_reg_led_off_h_ + channel_number * 4, counts >> 8); // shift upper byte into lower byte | |||||
} | |||||
return true; | |||||
} | } | ||||
hardware_interface::CallbackReturn | hardware_interface::CallbackReturn | ||||
@@ -64,7 +152,7 @@ std::vector<hardware_interface::StateInterface> | |||||
HexapodRobotHardwareInterface::export_state_interfaces() { | HexapodRobotHardwareInterface::export_state_interfaces() { | ||||
std::vector<hardware_interface::StateInterface> state_interfaces; | std::vector<hardware_interface::StateInterface> state_interfaces; | ||||
for (const hardware_interface::ComponentInfo &joint: info_.joints) { | for (const hardware_interface::ComponentInfo &joint: info_.joints) { | ||||
state_interfaces.emplace_back(hardware_interface::StateInterface(joint.name, hardware_interface::HW_IF_POSITION, &joint_states_[joint.name])); | |||||
state_interfaces.emplace_back(hardware_interface::StateInterface(joint.name, hardware_interface::HW_IF_POSITION, &joint_commands_[joint.name])); | |||||
} | } | ||||
return state_interfaces; | return state_interfaces; | ||||
} | } | ||||
@@ -81,14 +169,16 @@ HexapodRobotHardwareInterface::export_command_interfaces() { | |||||
hardware_interface::return_type | hardware_interface::return_type | ||||
HexapodRobotHardwareInterface::read(const rclcpp::Time &, const rclcpp::Duration &) { | HexapodRobotHardwareInterface::read(const rclcpp::Time &, const rclcpp::Duration &) { | ||||
for (const hardware_interface::ComponentInfo &joint: info_.joints) { | |||||
joint_states_[joint.name]=joint_commands_[joint.name]; | |||||
} | |||||
return hardware_interface::return_type::OK; | return hardware_interface::return_type::OK; | ||||
} | } | ||||
hardware_interface::return_type | hardware_interface::return_type | ||||
HexapodRobotHardwareInterface::write(const rclcpp::Time &, const rclcpp::Duration &) { | HexapodRobotHardwareInterface::write(const rclcpp::Time &, const rclcpp::Duration &) { | ||||
for (const hardware_interface::ComponentInfo &joint: info_.joints) { | |||||
const PWMOutput &pwm_output = joint_pwm_mapping_[joint.name]; | |||||
const double pwm_value = pwm_output.counts_intersect + joint_commands_[joint.name] * pwm_output.counts_slope; | |||||
pca_set_pwm_(pwm_output.i2c_address, pwm_output.channel, (uint16_t)pwm_value); | |||||
} | |||||
return hardware_interface::return_type::OK; | return hardware_interface::return_type::OK; | ||||
} | } | ||||
@@ -20,6 +20,8 @@ namespace hexapod_robot { | |||||
struct PWMOutput { | struct PWMOutput { | ||||
uint8_t i2c_address; | uint8_t i2c_address; | ||||
uint8_t channel; | uint8_t channel; | ||||
double counts_intersect; | |||||
double counts_slope; | |||||
}; | }; | ||||
class HexapodRobotHardwareInterface : public hardware_interface::SystemInterface { | class HexapodRobotHardwareInterface : public hardware_interface::SystemInterface { | ||||
@@ -39,15 +41,44 @@ public: | |||||
hardware_interface::return_type write(const rclcpp::Time & time, const rclcpp::Duration & period) override; | hardware_interface::return_type write(const rclcpp::Time & time, const rclcpp::Duration & period) override; | ||||
private: | private: | ||||
int i2c_fd_; | |||||
std::map<std::string, double> joint_commands_; | |||||
std::map<std::string, PWMOutput> joint_pwm_mapping_; | |||||
bool i2c_available_; | bool i2c_available_; | ||||
int i2c_fd_; | |||||
std::map<uint8_t, bool> pca9685_controller_available_; | |||||
std::map<uint8_t, bool> pca_available_; | |||||
std::map<std::string, double> joint_states_; | |||||
std::map<std::string, double> joint_commands_; | |||||
bool i2c_open_(const std::string& device); | |||||
std::map<std::string, PWMOutput> joint_pwm_mapping_; | |||||
const uint8_t pca_reg_mode1_ = 0x00; | |||||
const uint8_t pca_reg_mode2_ = 0x01; | |||||
const uint8_t pca_reg_prescale_ = 0xfe; | |||||
// base address for LED0 following, | |||||
// other channels can be calculated by adding | |||||
// channel_number * 4 | |||||
// to each base address | |||||
const uint8_t pca_reg_led_on_l_ = 0x06; | |||||
const uint8_t pca_reg_led_on_h_ = 0x07; | |||||
const uint8_t pca_reg_led_off_l_ = 0x08; | |||||
const uint8_t pca_reg_led_off_h_ = 0x09; | |||||
const uint8_t pca_mode1_sleep_ = 0x10; | |||||
const uint8_t pca_mode1_restart_ = 0x80; | |||||
const uint8_t pca_mode2_outdrv_ = 0x04; | |||||
const double pca_osc_frequency_ = 25000000; // 25 Mhz | |||||
const double pca_pwm_frequency_ = 50; // 50 Hz | |||||
const double pca_prescaler_value_ = (pca_osc_frequency_ / (4096.0 * pca_pwm_frequency_) - 0.5); // according to data sheet | |||||
const double pca_pwm_cycle_usec_ = 1000000 / pca_pwm_frequency_; // microseconds per PWM counter cycle | |||||
const double pca_pwm_count_usec_ = pca_pwm_cycle_usec_ / 4096; // microseconds per PWM single count | |||||
void pca_initialize_(); | |||||
bool pca_set_pwm_(uint8_t i2c_address, uint8_t channel_number, uint16_t counts); | |||||
}; | }; | ||||