| @@ -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); | |||||
| }; | }; | ||||