| @@ -32,6 +32,8 @@ ament_target_dependencies( | |||
| rclcpp_lifecycle | |||
| ) | |||
| target_link_libraries(hexapod_robot PUBLIC i2c) | |||
| pluginlib_export_plugin_description_file(hardware_interface hexapod_robot.xml) | |||
| @@ -75,7 +77,9 @@ ament_export_dependencies( | |||
| hardware_interface | |||
| pluginlib | |||
| rclcpp | |||
| rclcpp_lifecycle | |||
| rcl | |||
| cpp_lifecycle | |||
| i2c | |||
| ) | |||
| @@ -1,32 +1,58 @@ | |||
| controller_manager: | |||
| ros__parameters: | |||
| update_rate: 10 | |||
| update_rate: 50 | |||
| joint_state_broadcaster: | |||
| type: joint_state_broadcaster/JointStateBroadcaster | |||
| joint_trajectory_controller: | |||
| type: joint_trajectory_controller/JointTrajectoryController | |||
| forward_position_controller: | |||
| type: forward_command_controller/ForwardCommandController | |||
| forward_position_controller: | |||
| ros__parameters: | |||
| joints: | |||
| - t3_joint | |||
| interface_name: position | |||
| joint_trajectory_controller: | |||
| ros__parameters: | |||
| joints: | |||
| - c1_joint | |||
| - c2_joint | |||
| - c3_joint | |||
| - c4_joint | |||
| - c5_joint | |||
| - c6_joint | |||
| - f1_joint | |||
| - f2_joint | |||
| - f3_joint | |||
| - f4_joint | |||
| - f5_joint | |||
| - f6_joint | |||
| - t1_joint | |||
| - c2_joint | |||
| - f2_joint | |||
| - t2_joint | |||
| - c3_joint | |||
| - f3_joint | |||
| - t3_joint | |||
| - c4_joint | |||
| - f4_joint | |||
| - t4_joint | |||
| - c5_joint | |||
| - f5_joint | |||
| - t5_joint | |||
| - c6_joint | |||
| - f6_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_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( | |||
| package = "robot_state_publisher", | |||
| @@ -40,51 +33,69 @@ def generate_launch_description(): | |||
| 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 ( | |||
| package = "controller_manager", | |||
| executable = "spawner", | |||
| 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", | |||
| 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( | |||
| target_action = joint_state_broadcaster_spawner, | |||
| on_exit = [robot_controller_spawner] | |||
| on_exit = [joint_trajectory_controller_spawner] | |||
| ) | |||
| ) | |||
| nodes = [ | |||
| control_node, | |||
| robot_state_publisher_node, | |||
| 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) | |||
| @@ -55,11 +55,12 @@ | |||
| <material name="blue"/> | |||
| </visual> | |||
| </link> | |||
| <joint name="${name}_joint" type="continuous"> | |||
| <joint name="${name}_joint" type="revolute"> | |||
| <parent link="base_link"/> | |||
| <child link="${name}_link"/> | |||
| <origin xyz="${xy_offset} 0"/> | |||
| <axis xyz="0 0 1"/> | |||
| <limit effort="30" velocity="100" lower="-1" upper="1"/> | |||
| </joint> | |||
| </xacro:macro> | |||
| @@ -84,12 +85,13 @@ | |||
| </visual> | |||
| --> | |||
| </link> | |||
| <joint name="${name}_joint" type="continuous"> | |||
| <joint name="${name}_joint" type="revolute"> | |||
| <parent link="${parent_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 == 'r'}"><origin xyz=" 0.020 0.03 -0.0075"/></xacro:if> | |||
| <axis xyz="0 1 0"/> | |||
| <limit effort="30" velocity="100" lower="-1" upper="1"/> | |||
| </joint> | |||
| </xacro:macro> | |||
| @@ -141,12 +143,13 @@ | |||
| </visual> | |||
| </link> | |||
| <joint name="${name}_joint" type="continuous"> | |||
| <joint name="${name}_joint" type="revolute"> | |||
| <parent link="${parent_name}_link"/> | |||
| <child link="${name}_link"/> | |||
| <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> | |||
| <axis xyz="0 1 0"/> | |||
| <limit effort="30" velocity="100" lower="-1" upper="1"/> | |||
| </joint> | |||
| </xacro:macro> | |||
| @@ -27,44 +27,49 @@ | |||
| </joint> | |||
| </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_channel">${channel}</param> | |||
| <param name="${name}_joint_counts_intersect">${counts_intersect}</param> | |||
| <param name="${name}_joint_counts_slope">${counts_slope}</param> | |||
| </xacro:macro> | |||
| <ros2_control name="Hexapod" type="system"> | |||
| <hardware> | |||
| <plugin>hexapod_robot/HexapodRobotHardwareInterface</plugin> | |||
| <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> | |||
| <xacro:create_leg_joints number="1"/> | |||
| <xacro:create_leg_joints number="2"/> | |||
| <xacro:create_leg_joints number="3"/> | |||
| <xacro:create_leg_joints number="4"/> | |||
| <xacro:create_leg_joints number="5"/> | |||
| <xacro:create_leg_joints number="6"/> | |||
| </ros2_control> | |||
| </robot> | |||
| @@ -4,6 +4,18 @@ | |||
| #include "rclcpp/rclcpp.hpp" | |||
| #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 { | |||
| @@ -12,7 +24,7 @@ HexapodRobotHardwareInterface::on_init(const hardware_interface::HardwareInfo &i | |||
| if (hardware_interface::SystemInterface::on_init(info) != hardware_interface::CallbackReturn::SUCCESS) { | |||
| return hardware_interface::CallbackReturn::ERROR; | |||
| } | |||
| RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "on_init"); | |||
| 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 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; | |||
| 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; | |||
| 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) { | |||
| 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 | |||
| @@ -64,7 +152,7 @@ std::vector<hardware_interface::StateInterface> | |||
| HexapodRobotHardwareInterface::export_state_interfaces() { | |||
| std::vector<hardware_interface::StateInterface> state_interfaces; | |||
| 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; | |||
| } | |||
| @@ -81,14 +169,16 @@ HexapodRobotHardwareInterface::export_command_interfaces() { | |||
| hardware_interface::return_type | |||
| 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; | |||
| } | |||
| hardware_interface::return_type | |||
| 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; | |||
| } | |||
| @@ -20,6 +20,8 @@ namespace hexapod_robot { | |||
| struct PWMOutput { | |||
| uint8_t i2c_address; | |||
| uint8_t channel; | |||
| double counts_intersect; | |||
| double counts_slope; | |||
| }; | |||
| 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; | |||
| private: | |||
| int i2c_fd_; | |||
| std::map<std::string, double> joint_commands_; | |||
| std::map<std::string, PWMOutput> joint_pwm_mapping_; | |||
| 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); | |||
| }; | |||