| @@ -0,0 +1,2 @@ | |||||
| *~ | |||||
| .vscode | |||||
| @@ -0,0 +1,69 @@ | |||||
| cmake_minimum_required(VERSION 3.8) | |||||
| project(hexapod_hardware) | |||||
| 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(hardware_interface REQUIRED) | |||||
| find_package(pluginlib REQUIRED) | |||||
| find_package(rclcpp REQUIRED) | |||||
| find_package(rclcpp_lifecycle REQUIRED) | |||||
| add_library( | |||||
| hexapod_robot_hwi | |||||
| SHARED | |||||
| hardware/hexapod_robot_hwi.cpp | |||||
| ) | |||||
| target_compile_features(hexapod_robot_hwi PUBLIC cxx_std_17) | |||||
| target_include_directories(hexapod_robot_hwi PUBLIC | |||||
| $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/hardware/include> | |||||
| $<INSTALL_INTERFACE:include> | |||||
| ) | |||||
| ament_target_dependencies( | |||||
| hexapod_robot_hwi PUBLIC | |||||
| hardware_interface | |||||
| pluginlib | |||||
| rclcpp | |||||
| rclcpp_lifecycle | |||||
| ) | |||||
| target_link_libraries(hexapod_robot_hwi PUBLIC i2c) | |||||
| pluginlib_export_plugin_description_file(hardware_interface hexapod_hardware.xml) | |||||
| # install(TARGETS hexapod_robot_ik_server | |||||
| # DESTINATION lib/${PROJECT_NAME} | |||||
| # ) | |||||
| install(TARGETS hexapod_robot_hwi | |||||
| EXPORT export_hexapod_hardware | |||||
| ARCHIVE DESTINATION lib | |||||
| LIBRARY DESTINATION lib | |||||
| RUNTIME DESTINATION bin | |||||
| ) | |||||
| if(BUILD_TESTING) | |||||
| find_package(ament_lint_auto REQUIRED) | |||||
| set(ament_cmake_copyright_FOUND TRUE) | |||||
| set(ament_cmake_cpplint_FOUND TRUE) | |||||
| ament_lint_auto_find_test_dependencies() | |||||
| endif() | |||||
| ament_export_targets(export_hexapod_hardware HAS_LIBRARY_TARGET) | |||||
| ament_export_dependencies( | |||||
| hardware_interface | |||||
| pluginlib | |||||
| rclcpp | |||||
| rcl | |||||
| cpp_lifecycle | |||||
| i2c | |||||
| ) | |||||
| ament_package() | |||||
| @@ -0,0 +1,17 @@ | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in | |||||
| all copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||||
| THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
| THE SOFTWARE. | |||||
| @@ -0,0 +1,213 @@ | |||||
| #include "hexapod_robot/hexapod_robot_hwi.hpp" | |||||
| #include "hardware_interface/types/hardware_interface_type_values.hpp" | |||||
| #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 { | |||||
| HexapodRobotHardwareInterface::~HexapodRobotHardwareInterface() { | |||||
| on_deactivate(rclcpp_lifecycle::State()); | |||||
| } | |||||
| hardware_interface::CallbackReturn | |||||
| HexapodRobotHardwareInterface::on_init(const hardware_interface::HardwareInfo &info) { | |||||
| if (hardware_interface::SystemInterface::on_init(info) != hardware_interface::CallbackReturn::SUCCESS) { | |||||
| return hardware_interface::CallbackReturn::ERROR; | |||||
| } | |||||
| RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "on_init"); | |||||
| servos_powered_ = false; | |||||
| for (const hardware_interface::ComponentInfo &joint: info_.joints) { | |||||
| 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"; | |||||
| std::string counts_min_parameter = joint.name + "_counts_min"; | |||||
| std::string counts_max_parameter = joint.name + "_counts_max"; | |||||
| std::string startup_value_parameter = joint.name + "_startup_value"; | |||||
| 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.counts_intersect = stod(info_.hardware_parameters[counts_intersect_parameter]); | |||||
| pwm_output.counts_slope = stod(info_.hardware_parameters[counts_slope_parameter]); | |||||
| pwm_output.counts_min = stod(info_.hardware_parameters[counts_min_parameter]); | |||||
| pwm_output.counts_max = stod(info_.hardware_parameters[counts_max_parameter]); | |||||
| joint_commands_[joint.name] = stod(info_.hardware_parameters[startup_value_parameter]); | |||||
| 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. intersect: %.4f slope: %.4f", joint.name.c_str(), pwm_output.i2c_address, pwm_output.channel, pwm_output.counts_intersect, pwm_output.counts_slope); | |||||
| } | |||||
| i2c_available_ = i2c_open_(info_.hardware_parameters["i2c_device"]); | |||||
| if(i2c_available_) 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()); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| 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 | |||||
| HexapodRobotHardwareInterface::on_activate(const rclcpp_lifecycle::State &) { | |||||
| RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "on_activate"); | |||||
| servos_powered_ = true; | |||||
| return hardware_interface::CallbackReturn::SUCCESS; | |||||
| } | |||||
| hardware_interface::CallbackReturn | |||||
| HexapodRobotHardwareInterface::on_deactivate(const rclcpp_lifecycle::State &) { | |||||
| RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "on_deactivate"); | |||||
| servos_powered_ = false; | |||||
| write(rclcpp::Time(), rclcpp::Duration(std::chrono::nanoseconds(0))); | |||||
| return hardware_interface::CallbackReturn::SUCCESS; | |||||
| } | |||||
| 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_commands_[joint.name])); | |||||
| } | |||||
| return state_interfaces; | |||||
| } | |||||
| std::vector<hardware_interface::CommandInterface> | |||||
| HexapodRobotHardwareInterface::export_command_interfaces() { | |||||
| std::vector<hardware_interface::CommandInterface> command_interfaces; | |||||
| for (const hardware_interface::ComponentInfo &joint: info_.joints) { | |||||
| command_interfaces.emplace_back(hardware_interface::CommandInterface(joint.name, hardware_interface::HW_IF_POSITION, &joint_commands_[joint.name])); | |||||
| } | |||||
| return command_interfaces; | |||||
| } | |||||
| hardware_interface::return_type | |||||
| HexapodRobotHardwareInterface::read(const rclcpp::Time &, const rclcpp::Duration &) { | |||||
| 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]; | |||||
| if(servos_powered_) { | |||||
| double pwm_value = pwm_output.counts_intersect + joint_commands_[joint.name] * pwm_output.counts_slope; | |||||
| if(pwm_value < pwm_output.counts_min) { | |||||
| RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "write: pwm_value %.2f too low, clipped to %.2f", pwm_value, pwm_output.counts_min); | |||||
| pwm_value = pwm_output.counts_min; | |||||
| } | |||||
| if(pwm_value > pwm_output.counts_max) { | |||||
| RCLCPP_INFO(rclcpp::get_logger("HexapodRobotHardwareInterface"), "write: pwm_value %.2f too high, clipped to %.2f", pwm_value, pwm_output.counts_max); | |||||
| pwm_value = pwm_output.counts_max; | |||||
| } | |||||
| pca_set_pwm_(pwm_output.i2c_address, pwm_output.channel, (uint16_t)pwm_value); | |||||
| } else { | |||||
| pca_set_pwm_(pwm_output.i2c_address, pwm_output.channel, 0); | |||||
| } | |||||
| } | |||||
| return hardware_interface::return_type::OK; | |||||
| } | |||||
| } // namespace | |||||
| #include "pluginlib/class_list_macros.hpp" | |||||
| PLUGINLIB_EXPORT_CLASS(hexapod_robot::HexapodRobotHardwareInterface, hardware_interface::SystemInterface) | |||||
| @@ -0,0 +1,91 @@ | |||||
| #ifndef HEXAPOD_ROBOT_HWI_ | |||||
| #define HEXAPOD_ROBOT_HWI_ | |||||
| #include <memory> | |||||
| #include <string> | |||||
| #include <vector> | |||||
| #include <unordered_set> | |||||
| #include <map> | |||||
| #include "hardware_interface/handle.hpp" | |||||
| #include "hardware_interface/hardware_info.hpp" | |||||
| #include "hardware_interface/system_interface.hpp" | |||||
| #include "hardware_interface/types/hardware_interface_return_values.hpp" | |||||
| #include "rclcpp/macros.hpp" | |||||
| #include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp" | |||||
| #include "rclcpp_lifecycle/state.hpp" | |||||
| namespace hexapod_robot { | |||||
| struct PWMOutput { | |||||
| uint8_t i2c_address; | |||||
| uint8_t channel; | |||||
| double counts_intersect; | |||||
| double counts_slope; | |||||
| double counts_min; | |||||
| double counts_max; | |||||
| }; | |||||
| class HexapodRobotHardwareInterface : public hardware_interface::SystemInterface { | |||||
| public: | |||||
| RCLCPP_SHARED_PTR_DEFINITIONS(HexapodRobotHardwareInterface) | |||||
| virtual ~HexapodRobotHardwareInterface(); | |||||
| hardware_interface::CallbackReturn on_init(const hardware_interface::HardwareInfo &info) override; | |||||
| std::vector<hardware_interface::StateInterface> export_state_interfaces() override; | |||||
| std::vector<hardware_interface::CommandInterface> export_command_interfaces() override; | |||||
| hardware_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State &previous_state) override; | |||||
| hardware_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &previous_state) override; | |||||
| hardware_interface::return_type read(const rclcpp::Time & time, const rclcpp::Duration & period) override; | |||||
| hardware_interface::return_type write(const rclcpp::Time & time, const rclcpp::Duration & period) override; | |||||
| private: | |||||
| bool servos_powered_; | |||||
| 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> pca_available_; | |||||
| bool i2c_open_(const std::string& device); | |||||
| 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); | |||||
| }; | |||||
| } // namespace | |||||
| #endif // HEXAPOD_ROBOT_HWI_ | |||||
| @@ -0,0 +1,7 @@ | |||||
| <library path="hexapod_robot_hwi"> | |||||
| <class name="hexapod_robot/HexapodRobotHardwareInterface" | |||||
| type="hexapod_robot::HexapodRobotHardwareInterface" | |||||
| base_class_type="hardware_interface::SystemInterface"> | |||||
| <description>Hexapod Hardware Interface</description> | |||||
| </class> | |||||
| </library> | |||||
| @@ -0,0 +1,18 @@ | |||||
| <?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>hexapod_hardware</name> | |||||
| <version>0.9.0</version> | |||||
| <description>ROS2 Control Hardware Interface Plugin</description> | |||||
| <maintainer email="marcus@grieger.xyz">Marcus Grieger</maintainer> | |||||
| <license>MIT</license> | |||||
| <buildtool_depend>ament_cmake</buildtool_depend> | |||||
| <test_depend>ament_lint_auto</test_depend> | |||||
| <test_depend>ament_lint_common</test_depend> | |||||
| <export> | |||||
| <build_type>ament_cmake</build_type> | |||||
| </export> | |||||
| </package> | |||||
| @@ -0,0 +1,10 @@ | |||||
| moveit_setup_assistant_config: | |||||
| urdf: | |||||
| package: hexapod_robot | |||||
| relative_path: config/urdf/hexapod_robot.urdf.xacro | |||||
| srdf: | |||||
| relative_path: config/hexapod_robot.srdf | |||||
| package_settings: | |||||
| author_name: Marcus Grieger | |||||
| author_email: marcus@grieger.xyz | |||||
| generated_timestamp: 1707046355 | |||||
| @@ -0,0 +1,11 @@ | |||||
| cmake_minimum_required(VERSION 3.22) | |||||
| project(hexapod_robot_moveit) | |||||
| find_package(ament_cmake REQUIRED) | |||||
| ament_package() | |||||
| install(DIRECTORY launch DESTINATION share/${PROJECT_NAME} | |||||
| PATTERN "setup_assistant.launch" EXCLUDE) | |||||
| install(DIRECTORY config DESTINATION share/${PROJECT_NAME}) | |||||
| install(FILES .setup_assistant DESTINATION share/${PROJECT_NAME}) | |||||
| @@ -0,0 +1,669 @@ | |||||
| Panels: | |||||
| - Class: rviz_common/Displays | |||||
| Help Height: 70 | |||||
| Name: Displays | |||||
| Property Tree Widget: | |||||
| Expanded: | |||||
| - /MotionPlanning1 | |||||
| Splitter Ratio: 0.5 | |||||
| Tree Height: 378 | |||||
| - Class: rviz_common/Help | |||||
| Name: Help | |||||
| - Class: rviz_common/Views | |||||
| Expanded: | |||||
| - /Current View1 | |||||
| Name: Views | |||||
| Splitter Ratio: 0.5 | |||||
| Visualization Manager: | |||||
| Class: "" | |||||
| Displays: | |||||
| - Alpha: 0.5 | |||||
| Cell Size: 1 | |||||
| Class: rviz_default_plugins/Grid | |||||
| Color: 160; 160; 164 | |||||
| Enabled: true | |||||
| Line Style: | |||||
| Line Width: 0.029999999329447746 | |||||
| Value: Lines | |||||
| Name: Grid | |||||
| Normal Cell Count: 0 | |||||
| Offset: | |||||
| X: 0 | |||||
| Y: 0 | |||||
| Z: 0 | |||||
| Plane: XY | |||||
| Plane Cell Count: 10 | |||||
| Reference Frame: <Fixed Frame> | |||||
| Value: true | |||||
| - Acceleration_Scaling_Factor: 0.1 | |||||
| Class: moveit_rviz_plugin/MotionPlanning | |||||
| Enabled: true | |||||
| Move Group Namespace: "" | |||||
| MoveIt_Allow_Approximate_IK: false | |||||
| MoveIt_Allow_External_Program: false | |||||
| MoveIt_Allow_Replanning: false | |||||
| MoveIt_Allow_Sensor_Positioning: false | |||||
| MoveIt_Planning_Attempts: 10 | |||||
| MoveIt_Planning_Time: 5 | |||||
| MoveIt_Use_Cartesian_Path: false | |||||
| MoveIt_Use_Constraint_Aware_IK: false | |||||
| MoveIt_Workspace: | |||||
| Center: | |||||
| X: 0 | |||||
| Y: 0 | |||||
| Z: 0 | |||||
| Size: | |||||
| X: 2 | |||||
| Y: 2 | |||||
| Z: 2 | |||||
| Name: MotionPlanning | |||||
| Planned Path: | |||||
| Color Enabled: false | |||||
| Interrupt Display: false | |||||
| Links: | |||||
| All Links Enabled: true | |||||
| Expand Joint Details: false | |||||
| Expand Link Details: false | |||||
| Expand Tree: false | |||||
| Link Tree Style: Links in Alphabetic Order | |||||
| base_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| Loop Animation: true | |||||
| Robot Alpha: 0.5 | |||||
| Robot Color: 150; 50; 150 | |||||
| Show Robot Collision: false | |||||
| Show Robot Visual: true | |||||
| Show Trail: false | |||||
| State Display Time: 0.05 s | |||||
| Trail Step Size: 1 | |||||
| Trajectory Topic: display_planned_path | |||||
| Use Sim Time: false | |||||
| Planning Metrics: | |||||
| Payload: 1 | |||||
| Show Joint Torques: false | |||||
| Show Manipulability: false | |||||
| Show Manipulability Index: false | |||||
| Show Weight Limit: false | |||||
| TextHeight: 0.07999999821186066 | |||||
| Planning Request: | |||||
| Colliding Link Color: 255; 0; 0 | |||||
| Goal State Alpha: 1 | |||||
| Goal State Color: 250; 128; 0 | |||||
| Interactive Marker Size: 0.05000000074505806 | |||||
| Joint Violation Color: 255; 0; 255 | |||||
| Planning Group: e1_group | |||||
| Query Goal State: true | |||||
| Query Start State: false | |||||
| Show Workspace: false | |||||
| Start State Alpha: 1 | |||||
| Start State Color: 0; 255; 0 | |||||
| Planning Scene Topic: monitored_planning_scene | |||||
| Robot Description: robot_description | |||||
| Scene Geometry: | |||||
| Scene Alpha: 1 | |||||
| Scene Color: 50; 230; 50 | |||||
| Scene Display Time: 0.009999999776482582 | |||||
| Show Scene Geometry: true | |||||
| Voxel Coloring: Z-Axis | |||||
| Voxel Rendering: Occupied Voxels | |||||
| Scene Robot: | |||||
| Attached Body Color: 150; 50; 150 | |||||
| Links: | |||||
| All Links Enabled: true | |||||
| Expand Joint Details: false | |||||
| Expand Link Details: false | |||||
| Expand Tree: false | |||||
| Link Tree Style: Links in Alphabetic Order | |||||
| base_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| Robot Alpha: 0.5 | |||||
| Show Robot Collision: false | |||||
| Show Robot Visual: true | |||||
| Value: true | |||||
| Velocity_Scaling_Factor: 0.1 | |||||
| - Class: moveit_rviz_plugin/Trajectory | |||||
| Color Enabled: false | |||||
| Enabled: true | |||||
| Interrupt Display: false | |||||
| Links: | |||||
| All Links Enabled: true | |||||
| Expand Joint Details: false | |||||
| Expand Link Details: false | |||||
| Expand Tree: false | |||||
| Link Tree Style: Links in Alphabetic Order | |||||
| base_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| c6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| e6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| f6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t1_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t2_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t3_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t4_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t5_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| t6_link: | |||||
| Alpha: 1 | |||||
| Show Axes: false | |||||
| Show Trail: false | |||||
| Value: true | |||||
| Loop Animation: false | |||||
| Name: Trajectory | |||||
| Robot Alpha: 0.5 | |||||
| Robot Color: 150; 50; 150 | |||||
| Robot Description: robot_description | |||||
| Show Robot Collision: false | |||||
| Show Robot Visual: true | |||||
| Show Trail: false | |||||
| State Display Time: 3x | |||||
| Trail Step Size: 1 | |||||
| Trajectory Topic: /display_planned_path | |||||
| Use Sim Time: false | |||||
| Value: true | |||||
| - Class: rviz_default_plugins/TF | |||||
| Enabled: true | |||||
| Frame Timeout: 15 | |||||
| Frames: | |||||
| All Enabled: true | |||||
| base_link: | |||||
| Value: true | |||||
| c1_link: | |||||
| Value: true | |||||
| c2_link: | |||||
| Value: true | |||||
| c3_link: | |||||
| Value: true | |||||
| c4_link: | |||||
| Value: true | |||||
| c5_link: | |||||
| Value: true | |||||
| c6_link: | |||||
| Value: true | |||||
| e1_link: | |||||
| Value: true | |||||
| e2_link: | |||||
| Value: true | |||||
| e3_link: | |||||
| Value: true | |||||
| e4_link: | |||||
| Value: true | |||||
| e5_link: | |||||
| Value: true | |||||
| e6_link: | |||||
| Value: true | |||||
| f1_link: | |||||
| Value: true | |||||
| f2_link: | |||||
| Value: true | |||||
| f3_link: | |||||
| Value: true | |||||
| f4_link: | |||||
| Value: true | |||||
| f5_link: | |||||
| Value: true | |||||
| f6_link: | |||||
| Value: true | |||||
| t1_link: | |||||
| Value: true | |||||
| t2_link: | |||||
| Value: true | |||||
| t3_link: | |||||
| Value: true | |||||
| t4_link: | |||||
| Value: true | |||||
| t5_link: | |||||
| Value: true | |||||
| t6_link: | |||||
| Value: true | |||||
| Marker Scale: 0.10000000149011612 | |||||
| Name: TF | |||||
| Show Arrows: true | |||||
| Show Axes: true | |||||
| Show Names: false | |||||
| Tree: | |||||
| base_link: | |||||
| c1_link: | |||||
| f1_link: | |||||
| t1_link: | |||||
| e1_link: | |||||
| {} | |||||
| c2_link: | |||||
| f2_link: | |||||
| t2_link: | |||||
| e2_link: | |||||
| {} | |||||
| c3_link: | |||||
| f3_link: | |||||
| t3_link: | |||||
| e3_link: | |||||
| {} | |||||
| c4_link: | |||||
| f4_link: | |||||
| t4_link: | |||||
| e4_link: | |||||
| {} | |||||
| c5_link: | |||||
| f5_link: | |||||
| t5_link: | |||||
| e5_link: | |||||
| {} | |||||
| c6_link: | |||||
| f6_link: | |||||
| t6_link: | |||||
| e6_link: | |||||
| {} | |||||
| Update Interval: 0 | |||||
| Value: true | |||||
| Enabled: true | |||||
| Global Options: | |||||
| Background Color: 48; 48; 48 | |||||
| Fixed Frame: base_link | |||||
| Frame Rate: 30 | |||||
| Name: root | |||||
| Tools: | |||||
| - Class: rviz_default_plugins/Interact | |||||
| Hide Inactive Objects: true | |||||
| - Class: rviz_default_plugins/MoveCamera | |||||
| - Class: rviz_default_plugins/Select | |||||
| Transformation: | |||||
| Current: | |||||
| Class: rviz_default_plugins/TF | |||||
| Value: true | |||||
| Views: | |||||
| Current: | |||||
| Class: rviz_default_plugins/Orbit | |||||
| Distance: 0.6329568028450012 | |||||
| Enable Stereo Rendering: | |||||
| Stereo Eye Separation: 0.05999999865889549 | |||||
| Stereo Focal Distance: 1 | |||||
| Swap Stereo Eyes: false | |||||
| Value: false | |||||
| Focal Point: | |||||
| X: -0.07758110761642456 | |||||
| Y: 0.08254023641347885 | |||||
| Z: 0.09184139221906662 | |||||
| Focal Shape Fixed Size: true | |||||
| Focal Shape Size: 0.05000000074505806 | |||||
| Invert Z Axis: false | |||||
| Name: Current View | |||||
| Near Clip Distance: 0.009999999776482582 | |||||
| Pitch: 0.5400001406669617 | |||||
| Target Frame: base_link | |||||
| Value: Orbit (rviz_default_plugins) | |||||
| Yaw: 2.3151493072509766 | |||||
| Saved: ~ | |||||
| Window Geometry: | |||||
| " - Trajectory Slider": | |||||
| collapsed: false | |||||
| Displays: | |||||
| collapsed: false | |||||
| Height: 1287 | |||||
| Help: | |||||
| collapsed: false | |||||
| Hide Left Dock: false | |||||
| Hide Right Dock: false | |||||
| MotionPlanning: | |||||
| collapsed: false | |||||
| MotionPlanning - Trajectory Slider: | |||||
| collapsed: false | |||||
| QMainWindow State: 000000ff00000000fd0000000100000000000002b4000004a9fc0200000006fb00000044004d006f00740069006f006e0050006c0061006e006e0069006e00670020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000004400fffffffb000000100044006900730070006c006100790073010000003f00000200000000cc00fffffffb0000001c004d006f00740069006f006e0050006c0061006e006e0069006e00670100000245000001a10000019600fffffffb0000000800480065006c0070000000029a0000006e0000006f00fffffffb0000000a0056006900650077007301000003ec000000fc000000a900fffffffb000000280020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000004400ffffff0000063e000004a900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 | |||||
| Views: | |||||
| collapsed: false | |||||
| Width: 2296 | |||||
| X: 0 | |||||
| Y: 32 | |||||
| @@ -0,0 +1,21 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <robot name="hexapod_robot"> | |||||
| <group name="e1_group"> | |||||
| <chain base_link="base_link" tip_link="e1_link"/> | |||||
| </group> | |||||
| <group name="e2_group"> | |||||
| <chain base_link="base_link" tip_link="e2_link"/> | |||||
| </group> | |||||
| <group name="e3_group"> | |||||
| <chain base_link="base_link" tip_link="e3_link"/> | |||||
| </group> | |||||
| <group name="e4_group"> | |||||
| <chain base_link="base_link" tip_link="e4_link"/> | |||||
| </group> | |||||
| <group name="e5_group"> | |||||
| <chain base_link="base_link" tip_link="e5_link"/> | |||||
| </group> | |||||
| <group name="e6_group"> | |||||
| <chain base_link="base_link" tip_link="e6_link"/> | |||||
| </group> | |||||
| </robot> | |||||
| @@ -0,0 +1,14 @@ | |||||
| <?xml version="1.0"?> | |||||
| <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="hexapod_robot"> | |||||
| <xacro:arg name="initial_positions_file" default="initial_positions.yaml" /> | |||||
| <!-- Import hexapod_robot urdf file --> | |||||
| <xacro:include filename="$(find hexapod_robot)/config/urdf/hexapod_robot.urdf.xacro" /> | |||||
| <!-- Import control_xacro --> | |||||
| <xacro:include filename="hexapod_robot.ros2_control.xacro" /> | |||||
| <xacro:hexapod_robot_ros2_control name="FakeSystem" initial_positions_file="$(arg initial_positions_file)"/> | |||||
| </robot> | |||||
| @@ -0,0 +1,7 @@ | |||||
| # joint_limits.yaml allows the dynamics properties specified in the URDF to be overwritten or augmented as needed | |||||
| # For beginners, we downscale velocity and acceleration limits. | |||||
| # You can always specify higher scaling factors (<= 1.0) in your motion requests. # Increase the values below to 1.0 to always move at maximum speed. | |||||
| default_velocity_scaling_factor: 0.1 | |||||
| default_acceleration_scaling_factor: 0.1 | |||||
| @@ -0,0 +1,77 @@ | |||||
| e1_group: | |||||
| kinematics_solver: pick_ik/PickIkPlugin | |||||
| kinematics_solver_timeout: 0.05 | |||||
| kinematics_solver_attempts: 3 | |||||
| mode: global | |||||
| position_scale: 1.0 | |||||
| rotation_scale: 0.0 | |||||
| position_threshold: 0.001 | |||||
| orientation_threshold: 0.01 | |||||
| cost_threshold: 0.001 | |||||
| minimal_displacement_weight: 0.0 | |||||
| gd_step_size: 0.0001 | |||||
| e2_group: | |||||
| kinematics_solver: pick_ik/PickIkPlugin | |||||
| kinematics_solver_timeout: 0.05 | |||||
| kinematics_solver_attempts: 3 | |||||
| mode: global | |||||
| position_scale: 1.0 | |||||
| rotation_scale: 0.0 | |||||
| position_threshold: 0.001 | |||||
| orientation_threshold: 0.01 | |||||
| cost_threshold: 0.001 | |||||
| minimal_displacement_weight: 0.0 | |||||
| gd_step_size: 0.0001 | |||||
| e3_group: | |||||
| kinematics_solver: pick_ik/PickIkPlugin | |||||
| kinematics_solver_timeout: 0.05 | |||||
| kinematics_solver_attempts: 3 | |||||
| mode: global | |||||
| position_scale: 1.0 | |||||
| rotation_scale: 0.0 | |||||
| position_threshold: 0.001 | |||||
| orientation_threshold: 0.01 | |||||
| cost_threshold: 0.001 | |||||
| minimal_displacement_weight: 0.0 | |||||
| gd_step_size: 0.0001 | |||||
| e4_group: | |||||
| kinematics_solver: pick_ik/PickIkPlugin | |||||
| kinematics_solver_timeout: 0.05 | |||||
| kinematics_solver_attempts: 3 | |||||
| mode: global | |||||
| position_scale: 1.0 | |||||
| rotation_scale: 0.0 | |||||
| position_threshold: 0.001 | |||||
| orientation_threshold: 0.01 | |||||
| cost_threshold: 0.001 | |||||
| minimal_displacement_weight: 0.0 | |||||
| gd_step_size: 0.0001 | |||||
| e5_group: | |||||
| kinematics_solver: pick_ik/PickIkPlugin | |||||
| kinematics_solver_timeout: 0.05 | |||||
| kinematics_solver_attempts: 3 | |||||
| mode: global | |||||
| position_scale: 1.0 | |||||
| rotation_scale: 0.0 | |||||
| position_threshold: 0.001 | |||||
| orientation_threshold: 0.01 | |||||
| cost_threshold: 0.001 | |||||
| minimal_displacement_weight: 0.0 | |||||
| gd_step_size: 0.0001 | |||||
| e6_group: | |||||
| kinematics_solver: pick_ik/PickIkPlugin | |||||
| kinematics_solver_timeout: 0.05 | |||||
| kinematics_solver_attempts: 3 | |||||
| mode: global | |||||
| position_scale: 1.0 | |||||
| rotation_scale: 0.0 | |||||
| position_threshold: 0.001 | |||||
| orientation_threshold: 0.01 | |||||
| cost_threshold: 0.001 | |||||
| minimal_displacement_weight: 0.0 | |||||
| gd_step_size: 0.0001 | |||||
| @@ -0,0 +1,81 @@ | |||||
| # MoveIt uses this configuration for controller management | |||||
| moveit_controller_manager: moveit_simple_controller_manager/MoveItSimpleControllerManager | |||||
| moveit_simple_controller_manager: | |||||
| controller_names: | |||||
| - e1_group_controller | |||||
| - e2_group_controller | |||||
| - e3_group_controller | |||||
| - e4_group_controller | |||||
| - e5_group_controller | |||||
| - e6_group_controller | |||||
| e1_group_controller: | |||||
| type: FollowJointTrajectory | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| joints: | |||||
| - c1_joint | |||||
| - f1_joint | |||||
| - t1_joint | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| e2_group_controller: | |||||
| type: FollowJointTrajectory | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| joints: | |||||
| - c2_joint | |||||
| - f2_joint | |||||
| - t2_joint | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| e3_group_controller: | |||||
| type: FollowJointTrajectory | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| joints: | |||||
| - c3_joint | |||||
| - f3_joint | |||||
| - t3_joint | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| e4_group_controller: | |||||
| type: FollowJointTrajectory | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| joints: | |||||
| - c4_joint | |||||
| - f4_joint | |||||
| - t4_joint | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| e5_group_controller: | |||||
| type: FollowJointTrajectory | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| joints: | |||||
| - c5_joint | |||||
| - f5_joint | |||||
| - t5_joint | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| e6_group_controller: | |||||
| type: FollowJointTrajectory | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| joints: | |||||
| - c6_joint | |||||
| - f6_joint | |||||
| - t6_joint | |||||
| action_ns: follow_joint_trajectory | |||||
| default: true | |||||
| @@ -0,0 +1,6 @@ | |||||
| # Limits for the Pilz planner | |||||
| cartesian_limits: | |||||
| max_trans_vel: 1.0 | |||||
| max_trans_acc: 2.25 | |||||
| max_trans_dec: -5.0 | |||||
| max_rot_vel: 1.57 | |||||
| @@ -0,0 +1,103 @@ | |||||
| controller_manager: | |||||
| ros__parameters: | |||||
| update_rate: 50 | |||||
| joint_state_broadcaster: | |||||
| type: joint_state_broadcaster/JointStateBroadcaster | |||||
| e1_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| e2_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| e3_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| e4_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| e5_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| e6_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| e1_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c1_joint | |||||
| - f1_joint | |||||
| - t1_joint | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| e2_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c2_joint | |||||
| - f2_joint | |||||
| - t2_joint | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| e3_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c3_joint | |||||
| - f3_joint | |||||
| - t3_joint | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| e4_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c4_joint | |||||
| - f4_joint | |||||
| - t4_joint | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| e5_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c5_joint | |||||
| - f5_joint | |||||
| - t5_joint | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| e6_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c6_joint | |||||
| - f6_joint | |||||
| - t6_joint | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| @@ -0,0 +1,85 @@ | |||||
| from launch import LaunchDescription | |||||
| from launch.actions import RegisterEventHandler | |||||
| from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution | |||||
| from launch.event_handlers import OnProcessExit | |||||
| from launch_ros.actions import Node | |||||
| from launch_ros.substitutions import FindPackageShare | |||||
| from moveit_configs_utils import MoveItConfigsBuilder | |||||
| def generate_launch_description(): | |||||
| moveit_config = ( | |||||
| MoveItConfigsBuilder("hexapod_robot", package_name = "hexapod_robot") | |||||
| .planning_scene_monitor( | |||||
| publish_robot_description=True, publish_robot_description_semantic=True | |||||
| ) | |||||
| .planning_pipelines(pipelines = ["ompl"]) | |||||
| .to_moveit_configs() | |||||
| ) | |||||
| move_group_node = Node( | |||||
| package = "moveit_ros_move_group", | |||||
| executable = "move_group", | |||||
| output = "screen", | |||||
| parameters = [moveit_config.to_dict()] | |||||
| ) | |||||
| ros2_controllers = PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "ros2_controllers.yaml"]) | |||||
| robot_state_publisher_node = Node( | |||||
| package = "robot_state_publisher", | |||||
| executable = "robot_state_publisher", | |||||
| parameters = [moveit_config.robot_description], | |||||
| output = "both" | |||||
| ) | |||||
| ros2_control_node = Node( | |||||
| package = "controller_manager", | |||||
| executable = "ros2_control_node", | |||||
| parameters = [moveit_config.robot_description, ros2_controllers], | |||||
| output="both" | |||||
| ) | |||||
| joint_state_broadcaster_spawner = Node ( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["joint_state_broadcaster", "--controller-manager", "/controller_manager"] | |||||
| ) | |||||
| joint_trajectory_controller_spawner = Node( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["e1_group_controller", "e2_group_controller", "e3_group_controller", "e4_group_controller", "e5_group_controller", "e6_group_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_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
| event_handler = OnProcessExit( | |||||
| target_action = joint_state_broadcaster_spawner, | |||||
| on_exit = [joint_trajectory_controller_spawner] | |||||
| ) | |||||
| ) | |||||
| nodes = [ | |||||
| robot_state_publisher_node, | |||||
| move_group_node, | |||||
| ros2_control_node, | |||||
| joint_state_broadcaster_spawner, | |||||
| delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner | |||||
| ] | |||||
| return LaunchDescription(nodes) | |||||
| @@ -0,0 +1,96 @@ | |||||
| from launch import LaunchDescription | |||||
| from launch.actions import RegisterEventHandler | |||||
| from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution | |||||
| from launch.event_handlers import OnProcessExit | |||||
| from launch_ros.actions import Node | |||||
| from launch_ros.substitutions import FindPackageShare | |||||
| from moveit_configs_utils import MoveItConfigsBuilder | |||||
| def generate_launch_description(): | |||||
| moveit_config = ( | |||||
| MoveItConfigsBuilder("hexapod_robot", package_name = "hexapod_robot") | |||||
| .planning_scene_monitor( | |||||
| publish_robot_description=True, publish_robot_description_semantic=True | |||||
| ) | |||||
| .planning_pipelines(pipelines = ["ompl"]) | |||||
| .to_moveit_configs() | |||||
| ) | |||||
| move_group_node = Node( | |||||
| package = "moveit_ros_move_group", | |||||
| executable = "move_group", | |||||
| output = "screen", | |||||
| parameters = [moveit_config.to_dict()] | |||||
| ) | |||||
| ros2_controllers = PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "ros2_controllers.yaml"]) | |||||
| robot_state_publisher_node = Node( | |||||
| package = "robot_state_publisher", | |||||
| executable = "robot_state_publisher", | |||||
| parameters = [moveit_config.robot_description], | |||||
| output = "both" | |||||
| ) | |||||
| ros2_control_node = Node( | |||||
| package = "controller_manager", | |||||
| executable = "ros2_control_node", | |||||
| parameters = [moveit_config.robot_description, ros2_controllers], | |||||
| output="both" | |||||
| ) | |||||
| rviz_node = Node( | |||||
| package = "rviz2", | |||||
| executable = "rviz2", | |||||
| arguments = ['-d', PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "hexapod_robot.rviz"])], | |||||
| name = "rviz2", | |||||
| output = "screen", | |||||
| parameters = [moveit_config.planning_pipelines, moveit_config.robot_description_kinematics] | |||||
| ) | |||||
| joint_state_broadcaster_spawner = Node ( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["joint_state_broadcaster", "--controller-manager", "/controller_manager"] | |||||
| ) | |||||
| joint_trajectory_controller_spawner = Node( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["e1_group_controller", "e2_group_controller", "e3_group_controller", "e4_group_controller", "e5_group_controller", "e6_group_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_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
| event_handler = OnProcessExit( | |||||
| target_action = joint_state_broadcaster_spawner, | |||||
| on_exit = [joint_trajectory_controller_spawner] | |||||
| ) | |||||
| ) | |||||
| nodes = [ | |||||
| robot_state_publisher_node, | |||||
| move_group_node, | |||||
| ros2_control_node, | |||||
| 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) | |||||
| @@ -0,0 +1,37 @@ | |||||
| from launch import LaunchDescription | |||||
| from launch.actions import RegisterEventHandler | |||||
| from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution | |||||
| from launch.event_handlers import OnProcessExit | |||||
| from launch_ros.actions import Node | |||||
| from launch_ros.substitutions import FindPackageShare | |||||
| from moveit_configs_utils import MoveItConfigsBuilder | |||||
| def generate_launch_description(): | |||||
| moveit_config = ( | |||||
| MoveItConfigsBuilder("hexapod_robot", package_name = "hexapod_robot") | |||||
| .planning_scene_monitor( | |||||
| publish_robot_description=True, publish_robot_description_semantic=True | |||||
| ) | |||||
| .planning_pipelines(pipelines = ["ompl"]) | |||||
| .to_moveit_configs() | |||||
| ) | |||||
| rviz_node = Node( | |||||
| package = "rviz2", | |||||
| executable = "rviz2", | |||||
| arguments = ['-d', PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "hexapod_robot.rviz"])], | |||||
| name = "rviz2", | |||||
| output = "screen", | |||||
| parameters = [moveit_config.planning_pipelines, moveit_config.robot_description_kinematics] | |||||
| ) | |||||
| nodes = [ | |||||
| rviz_node | |||||
| ] | |||||
| return LaunchDescription(nodes) | |||||
| @@ -0,0 +1,52 @@ | |||||
| <?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>hexapod_robot_moveit</name> | |||||
| <version>0.3.0</version> | |||||
| <description> | |||||
| An automatically generated package with all the configuration and launch files for using the hexapod_robot with the MoveIt Motion Planning Framework | |||||
| </description> | |||||
| <maintainer email="marcus@grieger.xyz">mg</maintainer> | |||||
| <license>BSD</license> | |||||
| <url type="website">http://moveit.ros.org/</url> | |||||
| <url type="bugtracker">https://github.com/ros-planning/moveit2/issues</url> | |||||
| <url type="repository">https://github.com/ros-planning/moveit2</url> | |||||
| <author email="marcus@grieger.xyz">mg</author> | |||||
| <buildtool_depend>ament_cmake</buildtool_depend> | |||||
| <exec_depend>moveit_ros_move_group</exec_depend> | |||||
| <exec_depend>moveit_kinematics</exec_depend> | |||||
| <exec_depend>moveit_planners</exec_depend> | |||||
| <exec_depend>moveit_simple_controller_manager</exec_depend> | |||||
| <exec_depend>joint_state_publisher</exec_depend> | |||||
| <exec_depend>joint_state_publisher_gui</exec_depend> | |||||
| <exec_depend>tf2_ros</exec_depend> | |||||
| <exec_depend>xacro</exec_depend> | |||||
| <!-- The next 2 packages are required for the gazebo simulation. | |||||
| We don't include them by default to prevent installing gazebo and all its dependencies. --> | |||||
| <!-- <exec_depend>joint_trajectory_controller</exec_depend> --> | |||||
| <!-- <exec_depend>gazebo_ros_control</exec_depend> --> | |||||
| <exec_depend>controller_manager</exec_depend> | |||||
| <exec_depend>hexapod_robot</exec_depend> | |||||
| <exec_depend>moveit_configs_utils</exec_depend> | |||||
| <exec_depend>moveit_ros_move_group</exec_depend> | |||||
| <exec_depend>moveit_ros_visualization</exec_depend> | |||||
| <exec_depend>moveit_ros_warehouse</exec_depend> | |||||
| <exec_depend>moveit_setup_assistant</exec_depend> | |||||
| <exec_depend>robot_state_publisher</exec_depend> | |||||
| <exec_depend>rviz2</exec_depend> | |||||
| <exec_depend>rviz_common</exec_depend> | |||||
| <exec_depend>rviz_default_plugins</exec_depend> | |||||
| <exec_depend>tf2_ros</exec_depend> | |||||
| <exec_depend>warehouse_ros_mongo</exec_depend> | |||||
| <exec_depend>xacro</exec_depend> | |||||
| <export> | |||||
| <build_type>ament_cmake</build_type> | |||||
| </export> | |||||
| </package> | |||||
| @@ -0,0 +1,2 @@ | |||||
| *.pyc | |||||
| __pycache__ | |||||
| @@ -0,0 +1,17 @@ | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in | |||||
| all copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||||
| THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
| THE SOFTWARE. | |||||
| @@ -0,0 +1,97 @@ | |||||
| import math | |||||
| from geometry_msgs.msg import Point, Pose, Quaternion | |||||
| from trajectory_msgs.msg import JointTrajectoryPoint | |||||
| leg_offsets = { | |||||
| 'e1': Point(x = -40.0, y = 80.0, z = 25.0), | |||||
| 'e2': Point(x = -60.0, y = 0.0, z = 25.0), | |||||
| 'e3': Point(x = -40.0, y = -80.0, z = 25.0), | |||||
| 'e4': Point(x = 40.0, y = -80.0, z = 25.0), | |||||
| 'e5': Point(x = 60.0, y = 0.0, z = 25.0), | |||||
| 'e6': Point(x = 40.0, y = 80.0, z = 25.0) | |||||
| } | |||||
| leg_sides = { | |||||
| 'e1': 'l', | |||||
| 'e2': 'l', | |||||
| 'e3': 'l', | |||||
| 'e4': 'r', | |||||
| 'e5': 'r', | |||||
| 'e6': 'r', | |||||
| } | |||||
| def ik(body_pose): | |||||
| # todo apply rotation and translation of body | |||||
| joint_values = {} | |||||
| for l in body_pose['legs']: | |||||
| joint_values[l] = leg_ik( | |||||
| leg_sides[l], | |||||
| Point( | |||||
| x = body_pose['legs'][l].x - leg_offsets[l].x - body_pose['body'].position.x, | |||||
| y = body_pose['legs'][l].y - leg_offsets[l].y - body_pose['body'].position.y, | |||||
| z = body_pose['legs'][l].z - leg_offsets[l].z - body_pose['body'].position.z, | |||||
| ) | |||||
| ) | |||||
| return joint_values | |||||
| def leg_ik(side, point): | |||||
| x = point.x | |||||
| y = point.y | |||||
| z = point.z | |||||
| c = 20 | |||||
| f = 80 | |||||
| t = 130 | |||||
| ff = f*f | |||||
| tt = t*t | |||||
| if side == 'l': | |||||
| x = -x | |||||
| coxa_angle = math.atan2(y,x) | |||||
| r = math.sqrt(x*x + y * y) - c | |||||
| rr = r*r | |||||
| zz = z*z | |||||
| dd = rr + zz | |||||
| d = math.sqrt(dd) | |||||
| femur_angle = math.acos((ff + dd - tt) / (2 * f * d)) + math.acos(-z/d) - math.pi/2 | |||||
| tibia_angle = math.acos((tt + ff - dd) / (2 * t * f)) - math.pi/2 | |||||
| if(side == 'l'): | |||||
| coxa_angle *= -1 | |||||
| femur_angle *= -1 | |||||
| else: | |||||
| tibia_angle *= -1 | |||||
| return([coxa_angle, femur_angle, tibia_angle]) | |||||
| if __name__ == "__main__": | |||||
| print(leg_ik('l', Point(x = -100.0, y = 0.0, z = -130.0))) | |||||
| print(leg_ik('r', Point(x = 100.0, y = 0.0, z = -130.0))) | |||||
| print(leg_ik('l', Point(x = -120.0, y = 0.0, z = -130.0))) | |||||
| print(leg_ik('r', Point(x = 120.0, y = 0.0, z = -130.0))) | |||||
| for o in leg_offsets: | |||||
| print(leg_offsets[o]) | |||||
| print(ik(standing_pose)) | |||||
| print(ik(shutdown_pose)) | |||||
| poses = interpolate_pose(shutdown_pose, standing_pose , 10) | |||||
| for p in poses: | |||||
| print(p) | |||||
| print(ik(p)) | |||||
| print() | |||||
| @@ -0,0 +1,123 @@ | |||||
| import rclpy | |||||
| from rclpy.action import ActionClient | |||||
| from rclpy.node import Node | |||||
| from control_msgs.action import FollowJointTrajectory | |||||
| from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint | |||||
| from builtin_interfaces.msg import Duration | |||||
| from hexapod_python.poses import shutdown_pose, interpolate_pose | |||||
| from hexapod_python.ik import ik | |||||
| import time | |||||
| import copy | |||||
| import functools | |||||
| class IKNode(Node): | |||||
| def __init__(self, node_name = "ik_node"): | |||||
| super().__init__(node_name) | |||||
| self.leg_groups_ = [ "e1", "e2", "e3", "e4", "e5", "e6"] | |||||
| self.legs_active_ = False | |||||
| self.joint_names = [] | |||||
| for leg in self.leg_groups_: | |||||
| self.joint_names += [ f"c{leg[1:]}_joint", f"f{leg[1:]}_joint", f"t{leg[1:]}_joint" ] | |||||
| self.action_client_ = ActionClient(self, FollowJointTrajectory, "/joint_group_controller/follow_joint_trajectory") | |||||
| self.current_pose_ = copy.deepcopy(shutdown_pose) | |||||
| self.move_to_pose(shutdown_pose) | |||||
| def is_moving(self): | |||||
| return self.legs_active_ | |||||
| def move_to_pose(self, target_pose, interpolation_steps = 50, modulation_function = {}): | |||||
| if(self.is_moving()): | |||||
| print("move_to_pose: already moving") | |||||
| return | |||||
| self.follow_poses(interpolate_pose(self.current_pose_, target_pose, interpolation_steps, modulation_function)) | |||||
| def move_to_poses(self, target_poses, interpolation_steps = 50, modulation_function = {}): | |||||
| if(self.is_moving()): | |||||
| print("move_to_pose: already moving") | |||||
| return | |||||
| poses = [] | |||||
| t = self.current_pose_ | |||||
| for p in target_poses: | |||||
| poses += interpolate_pose(t, p, interpolation_steps, modulation_function) | |||||
| t = p | |||||
| self.follow_poses(poses) | |||||
| def follow_poses(self, poses): | |||||
| self.target_pose_ = poses[-1] | |||||
| ik_values = [ik(pose) for pose in poses] | |||||
| trajectory = self.create_joint_trajectory(ik_values) | |||||
| goal = FollowJointTrajectory.Goal(trajectory = trajectory) | |||||
| self.action_client_.wait_for_server() | |||||
| self.legs_active_ = True | |||||
| self.response_future = self.action_client_.send_goal_async(goal) | |||||
| self.response_future.add_done_callback(self.response_callback) | |||||
| def response_callback(self, future): | |||||
| goal_handle = future.result() | |||||
| if goal_handle.accepted: | |||||
| self.result_futures = future.result().get_result_async() | |||||
| self.result_futures.add_done_callback(self.result_callback) | |||||
| def result_callback(self, future): | |||||
| self.legs_active_ = False | |||||
| self.current_pose_ = self.target_pose_ | |||||
| def create_joint_trajectory(self, ik_values): | |||||
| t = 0 | |||||
| joint_trajectory_points = [] | |||||
| for iv in ik_values: | |||||
| joint_trajectory_positions = [] | |||||
| for leg in self.leg_groups_: | |||||
| joint_trajectory_positions += iv[leg] | |||||
| joint_trajectory_points.append( | |||||
| JointTrajectoryPoint( | |||||
| positions = joint_trajectory_positions, | |||||
| time_from_start = Duration(nanosec = t%50 * 20000000 , sec = int(t/50)) | |||||
| ) | |||||
| ) | |||||
| t += 1 | |||||
| return JointTrajectory( | |||||
| joint_names = self.joint_names, | |||||
| points = joint_trajectory_points | |||||
| ) | |||||
| def publish_ik_values(self, ik_values): | |||||
| for leg in self.leg_groups_: | |||||
| t = 0 | |||||
| joint_trajectory_points = [] | |||||
| for i in ik_values: | |||||
| joint_trajectory_points.append( | |||||
| JointTrajectoryPoint( | |||||
| positions = i[leg], | |||||
| time_from_start = Duration(nanosec = t%50 * 20000000 , sec = int(t/50)) | |||||
| ) | |||||
| ) | |||||
| t += 1 | |||||
| group_num = leg[1:] | |||||
| trajectory = JointTrajectory( | |||||
| joint_names = [ f"c{group_num}_joint", f"f{group_num}_joint", f"t{group_num}_joint" ], | |||||
| points = joint_trajectory_points | |||||
| ) | |||||
| self.publisher_[leg].publish(trajectory) | |||||
| @@ -0,0 +1,144 @@ | |||||
| from geometry_msgs.msg import Point, Pose, Quaternion | |||||
| from hexapod_python.ik import leg_offsets | |||||
| import copy | |||||
| import math | |||||
| standing_pose = { | |||||
| 'body': Pose( | |||||
| position = Point(x = 0.0, y = 0.0, z = 80.0 ), | |||||
| orientation = Quaternion(x = 0.0, y = 0.0, z = 0.0, w = 1.0) | |||||
| ), | |||||
| 'legs': { | |||||
| 'e1': Point(x = leg_offsets['e1'].x + -50.0, y = leg_offsets['e1'].y + 86.0, z = 0.0), | |||||
| 'e2': Point(x = leg_offsets['e2'].x + -100.0, y = leg_offsets['e2'].y + 0.0, z = 0.0), | |||||
| 'e3': Point(x = leg_offsets['e3'].x + -50.0, y = leg_offsets['e3'].y - 86.0, z = 0.0), | |||||
| 'e4': Point(x = leg_offsets['e4'].x + 50.0, y = leg_offsets['e4'].y - 86.0, z = 0.0), | |||||
| 'e5': Point(x = leg_offsets['e5'].x + 100.0, y = leg_offsets['e5'].y + 0.0, z = 0.0), | |||||
| 'e6': Point(x = leg_offsets['e6'].x + 50.0, y = leg_offsets['e6'].y + 86.0, z = 0.0) | |||||
| } | |||||
| } | |||||
| stand_up_pose = { | |||||
| 'body': Pose( | |||||
| position = Point(x = 0.0, y = 0.0, z = 80.0 ), | |||||
| orientation = Quaternion(x = 0.0, y = 0.0, z = 0.0, w = 1.0) | |||||
| ), | |||||
| 'legs': { | |||||
| 'e1': Point(x = leg_offsets['e1'].x + -70.0, y = leg_offsets['e1'].y + 121.0, z = 0.0), | |||||
| 'e2': Point(x = leg_offsets['e2'].x + -140.0, y = leg_offsets['e2'].y + 0.0, z = 0.0), | |||||
| 'e3': Point(x = leg_offsets['e3'].x + -70.0, y = leg_offsets['e3'].y - 121.0, z = 0.0), | |||||
| 'e4': Point(x = leg_offsets['e4'].x + 70.0, y = leg_offsets['e4'].y - 121.0, z = 0.0), | |||||
| 'e5': Point(x = leg_offsets['e5'].x + 140.0, y = leg_offsets['e5'].y + 0.0, z = 0.0), | |||||
| 'e6': Point(x = leg_offsets['e6'].x + 70.0, y = leg_offsets['e6'].y + 121.0, z = 0.0) | |||||
| } | |||||
| } | |||||
| shutdown_pose = { | |||||
| 'body': Pose( | |||||
| position = Point(x = 0.0, y = 0.0, z = -5.0 ), | |||||
| orientation = Quaternion(x = 0.0, y = 0.0, z = 0.0, w = 1.0) | |||||
| ), | |||||
| 'legs': { | |||||
| 'e1': Point(x = leg_offsets['e1'].x + -70.0, y = leg_offsets['e1'].y + 121.0, z = 0.0), | |||||
| 'e2': Point(x = leg_offsets['e2'].x + -140.0, y = leg_offsets['e2'].y + 0.0, z = 0.0), | |||||
| 'e3': Point(x = leg_offsets['e3'].x + -70.0, y = leg_offsets['e3'].y - 121.0, z = 0.0), | |||||
| 'e4': Point(x = leg_offsets['e4'].x + 70.0, y = leg_offsets['e4'].y - 121.0, z = 0.0), | |||||
| 'e5': Point(x = leg_offsets['e5'].x + 140.0, y = leg_offsets['e5'].y + 0.0, z = 0.0), | |||||
| 'e6': Point(x = leg_offsets['e6'].x + 70.0, y = leg_offsets['e6'].y + 121.0, z = 0.0) | |||||
| } | |||||
| } | |||||
| calibration_pose = { | |||||
| 'body': Pose( | |||||
| position = Point(x = 0.0, y = 0.0, z = 0.0 ), | |||||
| orientation = Quaternion(x = 0.0, y = 0.0, z = 0.0, w = 1.0) | |||||
| ), | |||||
| 'legs': { | |||||
| 'e1': Point(x = -200.0, y = 150.0, z = 0.0), | |||||
| 'e2': Point(x = -200.0, y = 0.0, z = 0.0), | |||||
| 'e3': Point(x = -200.0, y = -150.0, z = 0.0), | |||||
| 'e4': Point(x = 200.0, y = -150.0, z = 0.0), | |||||
| 'e5': Point(x = 200.0, y = 0.0, z = 0.0), | |||||
| 'e6': Point(x = 200.0, y = 150.0, z = 0.0), | |||||
| } | |||||
| } | |||||
| def interpolate_pose(start, end, steps, fn = {}): | |||||
| body_step_x = (end['body'].position.x - start['body'].position.x) / (steps-1) | |||||
| body_step_y = (end['body'].position.y - start['body'].position.y) / (steps-1) | |||||
| body_step_z = (end['body'].position.z - start['body'].position.z) / (steps-1) | |||||
| leg_step_x = {} | |||||
| leg_step_y = {} | |||||
| leg_step_z = {} | |||||
| for leg in start['legs']: | |||||
| leg_step_x[leg] = (end['legs'][leg].x - start['legs'][leg].x) / (steps-1) | |||||
| leg_step_y[leg] = (end['legs'][leg].y - start['legs'][leg].y) / (steps-1) | |||||
| leg_step_z[leg] = (end['legs'][leg].z - start['legs'][leg].z) / (steps-1) | |||||
| poses = [] | |||||
| for i in range(0, steps): | |||||
| pose = { | |||||
| 'body': Pose( | |||||
| position = Point( | |||||
| x = start['body'].position.x + body_step_x * i, | |||||
| y = start['body'].position.y + body_step_y * i, | |||||
| z = start['body'].position.z + body_step_z * i | |||||
| ), | |||||
| orientation = Quaternion(x = 0.0, y = 0.0, z = 0.0, w = 1.0) | |||||
| ), | |||||
| 'legs': {} | |||||
| } | |||||
| for leg in start['legs']: | |||||
| pose['legs'][leg] = Point( | |||||
| x = start['legs'][leg].x + leg_step_x[leg] * i, | |||||
| y = start['legs'][leg].y + leg_step_y[leg] * i, | |||||
| z = start['legs'][leg].z + leg_step_z[leg] * i | |||||
| ) | |||||
| if leg in fn: | |||||
| (fn_x, fn_y, fn_z) = fn[leg](i) | |||||
| pose['legs'][leg].x += fn_x | |||||
| pose['legs'][leg].y += fn_y | |||||
| pose['legs'][leg].z += fn_z | |||||
| poses.append(pose) | |||||
| return poses | |||||
| def step_poses(start_pose, target_pose, legs_at_once, leg_index_offset, interpolation_steps, step_height): | |||||
| poses = [] | |||||
| current_pose = start_pose | |||||
| n_legs = len(start_pose['legs']) | |||||
| n_cycles = int(n_legs / legs_at_once) | |||||
| for cycle in range(0, n_cycles): | |||||
| intermediate_pose = copy.deepcopy(current_pose) | |||||
| intermediate_pose['body'].position.x += (target_pose['body'].position.x - start_pose['body'].position.x) / n_cycles | |||||
| intermediate_pose['body'].position.y += (target_pose['body'].position.y - start_pose['body'].position.y) / n_cycles | |||||
| intermediate_pose['body'].position.z += (target_pose['body'].position.z - start_pose['body'].position.z) / n_cycles | |||||
| step_fn = {} | |||||
| for l in range(0, legs_at_once): # 0 .. 1 | |||||
| leg_num = (cycle + 1 + n_cycles * l + leg_index_offset) | |||||
| while(leg_num > n_legs): | |||||
| leg_num -= n_legs | |||||
| leg = f'e{leg_num}' | |||||
| intermediate_pose['legs'][leg] = target_pose['legs'][leg] | |||||
| step_fn[leg] = lambda i : (0, 0, step_height * math.sin(i/(interpolation_steps-1) * math.pi)) | |||||
| poses += interpolate_pose(current_pose, intermediate_pose, interpolation_steps, step_fn) | |||||
| current_pose = intermediate_pose | |||||
| return poses | |||||
| @@ -0,0 +1,132 @@ | |||||
| import rclpy | |||||
| from rclpy.node import Node | |||||
| from hexapod_python.ik_node import IKNode | |||||
| from hexapod_python.poses import standing_pose, stand_up_pose, shutdown_pose, interpolate_pose, step_poses | |||||
| from taranis.msg import TaranisPacket | |||||
| import math | |||||
| import time | |||||
| import copy | |||||
| import functools | |||||
| class TaranisTestNode(IKNode): | |||||
| MODE_SHUTDOWN = 0 | |||||
| MODE_STANDING = 1 | |||||
| MODE_WALKING = 2 | |||||
| def __init__(self): | |||||
| super().__init__('taranis_test') | |||||
| self.subscription = self.create_subscription( | |||||
| TaranisPacket, | |||||
| '/taranis_output', | |||||
| self.taranis_callback, | |||||
| 10 | |||||
| ) | |||||
| self.mode = self.MODE_SHUTDOWN | |||||
| def taranis_callback(self, msg): | |||||
| if self.is_moving(): | |||||
| return | |||||
| if self.mode == self.MODE_SHUTDOWN: | |||||
| if(msg.se == 1): | |||||
| self.follow_poses( | |||||
| interpolate_pose(self.current_pose_, stand_up_pose, 50) + | |||||
| step_poses(stand_up_pose, standing_pose, 2, 0, 25, 20) | |||||
| ) | |||||
| self.mode = self.MODE_STANDING | |||||
| print("-> MODE_STANDING") | |||||
| if self.mode == self.MODE_STANDING: | |||||
| if(msg.se == 0): | |||||
| self.follow_poses( | |||||
| step_poses(self.current_pose_, stand_up_pose, 2, 0, 25, 20) + | |||||
| interpolate_pose(stand_up_pose, shutdown_pose, 50) | |||||
| ) | |||||
| self.mode = self.MODE_SHUTDOWN | |||||
| print("-> MODE_SHUTDOWN") | |||||
| if(msg.se == 2): | |||||
| self.mode = self.MODE_WALKING | |||||
| self.second_step = False | |||||
| self.last_delta = 0 | |||||
| self.walking_base_pose = copy.deepcopy(self.current_pose_) | |||||
| print("-> MODE_WALKING") | |||||
| if(msg.se == 1): | |||||
| if(msg.sf == 1): | |||||
| target_pose = copy.deepcopy(self.current_pose_) | |||||
| target_pose['body'].position.x = msg.aileron * 20.0 | |||||
| target_pose['body'].position.y = msg.elevator * 20.0 | |||||
| target_pose['body'].position.z = standing_pose['body'].position.z + msg.throttle * 20 | |||||
| delta = math.sqrt( | |||||
| (target_pose['body'].position.x - self.current_pose_['body'].position.x)**2 + | |||||
| (target_pose['body'].position.y - self.current_pose_['body'].position.y)**2 + | |||||
| (target_pose['body'].position.z - self.current_pose_['body'].position.z)**2 | |||||
| ) | |||||
| if delta > 0.2: | |||||
| delta_steps = int(delta) * 2 | |||||
| if(delta_steps < 2): | |||||
| delta_steps = 2; | |||||
| self.move_to_pose(target_pose, delta_steps) | |||||
| if self.mode == self.MODE_WALKING: | |||||
| if(msg.se != 2 and self.last_delta == 0): | |||||
| self.mode = self.MODE_STANDING | |||||
| print("-> MODE_STANDING") | |||||
| return | |||||
| delta_x = msg.aileron * 100.0 | |||||
| delta_y = msg.elevator * 100.0 | |||||
| delta = math.sqrt(delta_x**2 + delta_y**2) | |||||
| if delta < 1: | |||||
| delta_x = 0.0 | |||||
| delta_y = 0.0 | |||||
| if delta > 10 or self.last_delta > 0: | |||||
| step_height = int(50 + 50 * msg.s1) | |||||
| interpolation_steps = int(60 + 50 * msg.s2) | |||||
| fn = lambda i : (0, 0, step_height * math.sin(i/(interpolation_steps-1) * math.pi)) | |||||
| lift_leg_group = ['e1', 'e3', 'e5'] | |||||
| step_pose = copy.deepcopy(self.walking_base_pose) | |||||
| step_fn = {} | |||||
| for leg in self.leg_groups_: | |||||
| if (leg in lift_leg_group) != self.second_step : | |||||
| step_pose['legs'][leg].x += delta_x / 2 | |||||
| step_pose['legs'][leg].y += delta_y / 2 | |||||
| step_fn[leg] = fn | |||||
| else: | |||||
| step_pose['legs'][leg].x -= delta_x / 2 | |||||
| step_pose['legs'][leg].y -= delta_y / 2 | |||||
| poses = interpolate_pose(self.current_pose_, step_pose, interpolation_steps, step_fn) | |||||
| self.follow_poses(poses) | |||||
| self.second_step = not self.second_step | |||||
| self.last_delta = delta | |||||
| def main(args = None): | |||||
| rclpy.init(args=args) | |||||
| ik_publisher = TaranisTestNode() | |||||
| rclpy.spin(ik_publisher) | |||||
| ik_publisher.destroy_node() | |||||
| rclpy.shutdown() | |||||
| @@ -0,0 +1,25 @@ | |||||
| <?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>hexapod_python</name> | |||||
| <version>0.0.0</version> | |||||
| <description>TODO: Package description</description> | |||||
| <maintainer email="marcus@grieger.xyz">wuselfuzz</maintainer> | |||||
| <license>MIT</license> | |||||
| <depend>taranis</depend> | |||||
| <exec_depend>rclpy</exec_depend> | |||||
| <exec_depend>std_msgs</exec_depend> | |||||
| <exec_depend>trajectory_msgs</exec_depend> | |||||
| <exec_depend>geometry_msgs</exec_depend> | |||||
| <test_depend>ament_copyright</test_depend> | |||||
| <test_depend>ament_flake8</test_depend> | |||||
| <test_depend>ament_pep257</test_depend> | |||||
| <test_depend>python3-pytest</test_depend> | |||||
| <export> | |||||
| <build_type>ament_python</build_type> | |||||
| </export> | |||||
| </package> | |||||
| @@ -0,0 +1,4 @@ | |||||
| [develop] | |||||
| script_dir=$base/lib/hexapod_python | |||||
| [install] | |||||
| install_scripts=$base/lib/hexapod_python | |||||
| @@ -0,0 +1,30 @@ | |||||
| from setuptools import find_packages, setup | |||||
| package_name = 'hexapod_python' | |||||
| setup( | |||||
| name=package_name, | |||||
| version='0.0.0', | |||||
| packages=find_packages(exclude=['test']), | |||||
| data_files=[ | |||||
| ('share/ament_index/resource_index/packages', | |||||
| ['resource/' + package_name]), | |||||
| ('share/' + package_name, ['package.xml']), | |||||
| ], | |||||
| install_requires=['setuptools'], | |||||
| zip_safe=True, | |||||
| maintainer='wuselfuzz', | |||||
| maintainer_email='marcus@grieger.xyz', | |||||
| description='TODO: Package description', | |||||
| license='MIT', | |||||
| tests_require=['pytest'], | |||||
| entry_points={ | |||||
| 'console_scripts': [ | |||||
| 'jtc_test = hexapod_python.jtc_test:main', | |||||
| 'ik_test = hexapod_python.ik_test:main', | |||||
| 'ik_calibration = hexapod_python.ik_calibration:main', | |||||
| 'step_test = hexapod_python.step_test:main', | |||||
| 'taranis_test = hexapod_python.taranis_test:main' | |||||
| ], | |||||
| }, | |||||
| ) | |||||
| @@ -0,0 +1,25 @@ | |||||
| # Copyright 2015 Open Source Robotics Foundation, Inc. | |||||
| # | |||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| # you may not use this file except in compliance with the License. | |||||
| # You may obtain a copy of the License at | |||||
| # | |||||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||||
| # | |||||
| # Unless required by applicable law or agreed to in writing, software | |||||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| # See the License for the specific language governing permissions and | |||||
| # limitations under the License. | |||||
| from ament_copyright.main import main | |||||
| import pytest | |||||
| # Remove the `skip` decorator once the source file(s) have a copyright header | |||||
| @pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') | |||||
| @pytest.mark.copyright | |||||
| @pytest.mark.linter | |||||
| def test_copyright(): | |||||
| rc = main(argv=['.', 'test']) | |||||
| assert rc == 0, 'Found errors' | |||||
| @@ -0,0 +1,25 @@ | |||||
| # Copyright 2017 Open Source Robotics Foundation, Inc. | |||||
| # | |||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| # you may not use this file except in compliance with the License. | |||||
| # You may obtain a copy of the License at | |||||
| # | |||||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||||
| # | |||||
| # Unless required by applicable law or agreed to in writing, software | |||||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| # See the License for the specific language governing permissions and | |||||
| # limitations under the License. | |||||
| from ament_flake8.main import main_with_errors | |||||
| import pytest | |||||
| @pytest.mark.flake8 | |||||
| @pytest.mark.linter | |||||
| def test_flake8(): | |||||
| rc, errors = main_with_errors(argv=[]) | |||||
| assert rc == 0, \ | |||||
| 'Found %d code style errors / warnings:\n' % len(errors) + \ | |||||
| '\n'.join(errors) | |||||
| @@ -0,0 +1,23 @@ | |||||
| # Copyright 2015 Open Source Robotics Foundation, Inc. | |||||
| # | |||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| # you may not use this file except in compliance with the License. | |||||
| # You may obtain a copy of the License at | |||||
| # | |||||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||||
| # | |||||
| # Unless required by applicable law or agreed to in writing, software | |||||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| # See the License for the specific language governing permissions and | |||||
| # limitations under the License. | |||||
| from ament_pep257.main import main | |||||
| import pytest | |||||
| @pytest.mark.linter | |||||
| @pytest.mark.pep257 | |||||
| def test_pep257(): | |||||
| rc = main(argv=['.', 'test']) | |||||
| assert rc == 0, 'Found code style errors / warnings' | |||||
| @@ -0,0 +1,16 @@ | |||||
| cmake_minimum_required(VERSION 3.8) | |||||
| project(hexapod_robot) | |||||
| 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) | |||||
| install( | |||||
| DIRECTORY config launch | |||||
| DESTINATION share/${PROJECT_NAME} | |||||
| ) | |||||
| ament_package() | |||||
| @@ -0,0 +1,39 @@ | |||||
| controller_manager: | |||||
| ros__parameters: | |||||
| update_rate: 50 | |||||
| joint_state_broadcaster: | |||||
| type: joint_state_broadcaster/JointStateBroadcaster | |||||
| joint_group_controller: | |||||
| type: joint_trajectory_controller/JointTrajectoryController | |||||
| joint_group_controller: | |||||
| ros__parameters: | |||||
| joints: | |||||
| - c1_joint | |||||
| - f1_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 | |||||
| command_interfaces: | |||||
| - position | |||||
| state_interfaces: | |||||
| - position | |||||
| interpolation_method: none | |||||
| @@ -0,0 +1,28 @@ | |||||
| <?xml version="1.0"?> | |||||
| <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="hexapod_robot"> | |||||
| <xacro:include filename="$(find hexapod_robot)/config/urdf/hexapod_robot_materials.xacro"/> | |||||
| <xacro:include filename="$(find hexapod_robot)/config/urdf/hexapod_robot_macros.xacro"/> | |||||
| <link name="base_link"> | |||||
| <xacro:create_frame_plate z_offset="-0.034"/> | |||||
| <xacro:create_frame_plate z_offset=" 0.034"/> | |||||
| <xacro:create_coxa_joint_mount xy_offset="-0.040 0.080"/> | |||||
| <xacro:create_coxa_joint_mount xy_offset="-0.060 0.000"/> | |||||
| <xacro:create_coxa_joint_mount xy_offset="-0.040 -0.080"/> | |||||
| <xacro:create_coxa_joint_mount xy_offset=" 0.040 -0.080"/> | |||||
| <xacro:create_coxa_joint_mount xy_offset=" 0.060 0.000"/> | |||||
| <xacro:create_coxa_joint_mount xy_offset=" 0.040 0.080"/> | |||||
| </link> | |||||
| <xacro:create_leg number="1" side="l" xy_offset="-0.040 0.080"/> | |||||
| <xacro:create_leg number="2" side="l" xy_offset="-0.060 0.000"/> | |||||
| <xacro:create_leg number="3" side="l" xy_offset="-0.040 -0.080"/> | |||||
| <xacro:create_leg number="4" side="r" xy_offset=" 0.040 -0.080"/> | |||||
| <xacro:create_leg number="5" side="r" xy_offset=" 0.060 0.000"/> | |||||
| <xacro:create_leg number="6" side="r" xy_offset=" 0.040 0.080"/> | |||||
| <xacro:include filename="$(find hexapod_robot)/config/urdf/hexapod_robot_ros2_control.xacro"/> | |||||
| </robot> | |||||
| @@ -0,0 +1,179 @@ | |||||
| <?xml version="1.0"?> | |||||
| <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> | |||||
| <xacro:macro name="create_frame_plate" params="z_offset"> | |||||
| <visual> | |||||
| <origin xyz="0 0 ${z_offset}"/> | |||||
| <geometry> | |||||
| <box size="0.100 0.180 0.003"/> | |||||
| </geometry> | |||||
| <material name="black"/> | |||||
| </visual> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="create_coxa_joint_mount" params="xy_offset"> | |||||
| <visual> | |||||
| <origin xyz="${xy_offset} -0.0325"/> | |||||
| <geometry> | |||||
| <cylinder radius="0.0125" length="0.006"/> | |||||
| </geometry> | |||||
| <material name="black"/> | |||||
| </visual> | |||||
| <visual> | |||||
| <origin xyz="${xy_offset} 0.0325"/> | |||||
| <geometry> | |||||
| <cylinder radius="0.0125" length="0.006"/> | |||||
| </geometry> | |||||
| <material name="black"/> | |||||
| </visual> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="create_coxa" params="name side xy_offset"> | |||||
| <link name="${name}_link"> | |||||
| <!-- coxa bounding box --> | |||||
| <visual> | |||||
| <geometry> | |||||
| <box size="0.045 0.045 0.045"/> | |||||
| </geometry> | |||||
| <xacro:if value="${side == 'l'}"><origin xyz="-0.015 -0.005 0"/></xacro:if> | |||||
| <xacro:if value="${side == 'r'}"><origin xyz=" 0.015 -0.005 0"/></xacro:if> | |||||
| <material name="blue"/> | |||||
| </visual> | |||||
| <!-- coxa rotation axis --> | |||||
| <visual> | |||||
| <geometry> | |||||
| <cylinder radius="0.005" length="0.065"/> | |||||
| </geometry> | |||||
| <material name="blue"/> | |||||
| </visual> | |||||
| <!-- femur rotation axis --> | |||||
| <visual> | |||||
| <xacro:if value="${side == 'l'}"><origin xyz="-0.020 0 -0.0075" rpy="-1.57 0 0"/></xacro:if> | |||||
| <xacro:if value="${side == 'r'}"><origin xyz=" 0.020 0 -0.0075" rpy="-1.57 0 0"/></xacro:if> | |||||
| <geometry> | |||||
| <cylinder radius="0.005" length="0.065"/> | |||||
| </geometry> | |||||
| <material name="blue"/> | |||||
| </visual> | |||||
| </link> | |||||
| <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.57" upper="1.57"/> | |||||
| </joint> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="create_femur" params="name parent_name side"> | |||||
| <link name="${name}_link"> | |||||
| <visual> | |||||
| <xacro:if value="${side == 'l'}"><origin xyz="-0.040 0 0"/></xacro:if> | |||||
| <xacro:if value="${side == 'r'}"><origin xyz=" 0.040 0 0"/></xacro:if> | |||||
| <geometry> | |||||
| <box size="0.100 0.003 0.010"/> | |||||
| </geometry> | |||||
| <material name="green"/> | |||||
| </visual> | |||||
| <!-- | |||||
| <visual> | |||||
| <xacro:if value="${side == 'l'}"><origin xyz="-0.040 -0.003 0" rpy="-1.57 0 0"/></xacro:if> | |||||
| <xacro:if value="${side == 'r'}"><origin xyz=" 0.040 -0.003 0" rpy="-1.57 0 0"/></xacro:if> | |||||
| <geometry> | |||||
| <cylinder radius="0.0125" length="0.006"/> | |||||
| </geometry> | |||||
| <material name="green"/> | |||||
| </visual> | |||||
| --> | |||||
| </link> | |||||
| <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.57" upper="1.57"/> | |||||
| </joint> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="create_tibia" params="name parent_name side"> | |||||
| <link name="${name}_link"> | |||||
| <!-- servo box --> | |||||
| <visual> | |||||
| <origin xyz="0 -0.005 0.010"/> | |||||
| <geometry> | |||||
| <box size="0.020 0.035 0.040"/> | |||||
| </geometry> | |||||
| <material name="red"/> | |||||
| </visual> | |||||
| <!-- tibia top --> | |||||
| <visual> | |||||
| <xacro:if value="${side == 'l'}"><origin xyz="-0.005 0 0.0025"/></xacro:if> | |||||
| <xacro:if value="${side == 'r'}"><origin xyz=" 0.005 0 0.0025"/></xacro:if> | |||||
| <geometry> | |||||
| <box size="0.040 0.003 0.075"/> | |||||
| </geometry> | |||||
| <material name="red"/> | |||||
| </visual> | |||||
| <!-- tibia middle --> | |||||
| <visual> | |||||
| <xacro:if value="${side == 'l'}"><origin xyz="-0.005 0 -0.055"/></xacro:if> | |||||
| <xacro:if value="${side == 'r'}"><origin xyz=" 0.005 0 -0.055"/></xacro:if> | |||||
| <origin xyz="0 0 -0.055"/> | |||||
| <geometry> | |||||
| <box size="0.030 0.003 0.050"/> | |||||
| </geometry> | |||||
| <material name="red"/> | |||||
| </visual> | |||||
| <!-- tibia bottom --> | |||||
| <visual> | |||||
| <origin xyz=" 0.0 0 -0.105"/> | |||||
| <origin xyz="0 0 -0.105"/> | |||||
| <geometry> | |||||
| <box size="0.010 0.003 0.050"/> | |||||
| </geometry> | |||||
| <material name="red"/> | |||||
| </visual> | |||||
| <!-- tibia rotation axis --> | |||||
| <visual> | |||||
| <origin xyz="0 0 0" rpy="-1.57 0 0"/> | |||||
| <geometry> | |||||
| <cylinder radius="0.005" length="0.065"/> | |||||
| </geometry> | |||||
| <material name="red"/> | |||||
| </visual> | |||||
| </link> | |||||
| <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.57" upper="1.57"/> | |||||
| </joint> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="create_end" params="name side parent_name"> | |||||
| <link name="${name}_link"> | |||||
| <visual> | |||||
| <geometry> | |||||
| <sphere radius="0.004"/> | |||||
| </geometry> | |||||
| <material name="cyan"/> | |||||
| </visual> | |||||
| </link> | |||||
| <joint name="${name}_joint" type="fixed"> | |||||
| <parent link="${parent_name}_link"/> | |||||
| <child link="${name}_link"/> | |||||
| <origin xyz="0 0 -0.130"/> | |||||
| </joint> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="create_leg" params="number side xy_offset"> | |||||
| <xacro:create_coxa name="c${number}" side="${side}" xy_offset="${xy_offset}"/> | |||||
| <xacro:create_femur name="f${number}" side="${side}" parent_name="c${number}"/> | |||||
| <xacro:create_tibia name="t${number}" side="${side}" parent_name="f${number}"/> | |||||
| <xacro:create_end name="e${number}" side="${side}" parent_name="t${number}"/> | |||||
| </xacro:macro> | |||||
| </robot> | |||||
| @@ -0,0 +1,25 @@ | |||||
| <?xml version="1.0"?> | |||||
| <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> | |||||
| <material name="black"> | |||||
| <color rgba="0 0 0 1"/> | |||||
| </material> | |||||
| <material name="blue"> | |||||
| <color rgba="0 0 1 1"/> | |||||
| </material> | |||||
| <material name="green"> | |||||
| <color rgba="0 1 0 1"/> | |||||
| </material> | |||||
| <material name="red"> | |||||
| <color rgba="1 0 0 1"/> | |||||
| </material> | |||||
| <material name="cyan"> | |||||
| <color rgba="1 0 1 1"/> | |||||
| </material> | |||||
| </robot> | |||||
| @@ -0,0 +1,78 @@ | |||||
| <?xml version="1.0"?> | |||||
| <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> | |||||
| <xacro:macro name="create_leg_joints" params="number"> | |||||
| <joint name="c${number}_joint"> | |||||
| <command_interface name="position"> | |||||
| <param name="min">-1.57</param> | |||||
| <param name="max">1.57</param> | |||||
| </command_interface> | |||||
| <state_interface name="position"/> | |||||
| </joint> | |||||
| <joint name="f${number}_joint"> | |||||
| <command_interface name="position"> | |||||
| <param name="min">-1.57</param> | |||||
| <param name="max">1.57</param> | |||||
| </command_interface> | |||||
| <state_interface name="position"/> | |||||
| </joint> | |||||
| <joint name="t${number}_joint"> | |||||
| <command_interface name="position"> | |||||
| <param name="min">-1.57</param> | |||||
| <param name="max">1.57</param> | |||||
| </command_interface> | |||||
| <state_interface name="position"/> | |||||
| </joint> | |||||
| </xacro:macro> | |||||
| <xacro:macro name="map_joint_name_to_hardware" params="name i2c_address channel counts_intersect counts_slope counts_min counts_max startup_value"> | |||||
| <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> | |||||
| <param name="${name}_joint_counts_min">${counts_min}</param> | |||||
| <param name="${name}_joint_counts_max">${counts_max}</param> | |||||
| <param name="${name}_joint_startup_value">${startup_value}</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" counts_intersect="389" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="f1" i2c_address="0x40" channel="14" counts_intersect="329" counts_slope="129" counts_min="125" counts_max="550" startup_value="-1.2"/> | |||||
| <xacro:map_joint_name_to_hardware name="t1" i2c_address="0x40" channel="15" counts_intersect="202" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="c2" i2c_address="0x40" channel="9" counts_intersect="329" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="f2" i2c_address="0x40" channel="10" counts_intersect="334" counts_slope="129" counts_min="125" counts_max="550" startup_value="-1.2"/> | |||||
| <xacro:map_joint_name_to_hardware name="t2" i2c_address="0x40" channel="11" counts_intersect="232" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="c3" i2c_address="0x40" channel="2" counts_intersect="238" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="f3" i2c_address="0x40" channel="1" counts_intersect="320" counts_slope="129" counts_min="125" counts_max="550" startup_value="-1.2"/> | |||||
| <xacro:map_joint_name_to_hardware name="t3" i2c_address="0x40" channel="0" counts_intersect="279" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="c4" i2c_address="0x41" channel="13" counts_intersect="448" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="f4" i2c_address="0x41" channel="14" counts_intersect="371" counts_slope="129" counts_min="125" counts_max="550" startup_value="1.2"/> | |||||
| <xacro:map_joint_name_to_hardware name="t4" i2c_address="0x41" channel="15" counts_intersect="479" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="c5" i2c_address="0x41" channel="6" counts_intersect="355" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="f5" i2c_address="0x41" channel="5" counts_intersect="363" counts_slope="129" counts_min="125" counts_max="550" startup_value="1.2"/> | |||||
| <xacro:map_joint_name_to_hardware name="t5" i2c_address="0x41" channel="4" counts_intersect="424" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="c6" i2c_address="0x41" channel="2" counts_intersect="252" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| <xacro:map_joint_name_to_hardware name="f6" i2c_address="0x41" channel="1" counts_intersect="335" counts_slope="129" counts_min="125" counts_max="550" startup_value="1.2"/> | |||||
| <xacro:map_joint_name_to_hardware name="t6" i2c_address="0x41" channel="0" counts_intersect="447" counts_slope="129" counts_min="125" counts_max="550" startup_value="0"/> | |||||
| </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> | |||||
| @@ -0,0 +1,71 @@ | |||||
| from launch import LaunchDescription | |||||
| from launch.actions import RegisterEventHandler | |||||
| from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution | |||||
| from launch.event_handlers import OnProcessExit | |||||
| from launch_ros.actions import Node | |||||
| from launch_ros.substitutions import FindPackageShare | |||||
| def generate_launch_description(): | |||||
| robot_description_content = Command([ | |||||
| PathJoinSubstitution([FindExecutable(name="xacro")]), | |||||
| " ", | |||||
| PathJoinSubstitution( | |||||
| [ | |||||
| FindPackageShare("hexapod_robot"), | |||||
| "config", | |||||
| "urdf", | |||||
| "hexapod_robot.urdf.xacro" | |||||
| ] | |||||
| ) | |||||
| ] | |||||
| ) | |||||
| robot_description = {"robot_description": robot_description_content} | |||||
| robot_controllers = PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "ros2_controllers.yaml"]) | |||||
| robot_state_publisher_node = Node( | |||||
| package = "robot_state_publisher", | |||||
| executable = "robot_state_publisher", | |||||
| parameters = [robot_description], | |||||
| output = "both" | |||||
| ) | |||||
| control_node = Node( | |||||
| package = "controller_manager", | |||||
| executable = "ros2_control_node", | |||||
| parameters = [robot_description, robot_controllers], | |||||
| output="both" | |||||
| ) | |||||
| joint_state_broadcaster_spawner = Node ( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["joint_state_broadcaster", "--controller-manager", "/controller_manager"] | |||||
| ) | |||||
| joint_trajectory_controller_spawner = Node( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["joint_group_controller", "--controller-manager", "/controller_manager"] | |||||
| ) | |||||
| delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
| event_handler = OnProcessExit( | |||||
| target_action = joint_state_broadcaster_spawner, | |||||
| on_exit = [joint_trajectory_controller_spawner] | |||||
| ) | |||||
| ) | |||||
| nodes = [ | |||||
| control_node, | |||||
| robot_state_publisher_node, | |||||
| joint_state_broadcaster_spawner, | |||||
| delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner | |||||
| ] | |||||
| return LaunchDescription(nodes) | |||||
| @@ -0,0 +1,85 @@ | |||||
| from launch import LaunchDescription | |||||
| from launch.actions import RegisterEventHandler | |||||
| from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution | |||||
| from launch.event_handlers import OnProcessExit | |||||
| from launch_ros.actions import Node | |||||
| from launch_ros.substitutions import FindPackageShare | |||||
| def generate_launch_description(): | |||||
| robot_description_content = Command([ | |||||
| PathJoinSubstitution([FindExecutable(name="xacro")]), | |||||
| " ", | |||||
| PathJoinSubstitution( | |||||
| [ | |||||
| FindPackageShare("hexapod_robot"), | |||||
| "config", | |||||
| "urdf", | |||||
| "hexapod_robot.urdf.xacro" | |||||
| ] | |||||
| ) | |||||
| ] | |||||
| ) | |||||
| robot_description = {"robot_description": robot_description_content} | |||||
| robot_controllers = PathJoinSubstitution([FindPackageShare("hexapod_robot"), "config", "ros2_controllers.yaml"]) | |||||
| robot_state_publisher_node = Node( | |||||
| package = "robot_state_publisher", | |||||
| executable = "robot_state_publisher", | |||||
| parameters = [robot_description], | |||||
| output = "both" | |||||
| ) | |||||
| control_node = Node( | |||||
| package = "controller_manager", | |||||
| executable = "ros2_control_node", | |||||
| parameters = [robot_description, robot_controllers], | |||||
| output = "both" | |||||
| ) | |||||
| taranis_node = Node( | |||||
| package = "taranis", | |||||
| executable = "taranis_node", | |||||
| output = "both" | |||||
| ) | |||||
| ik_node = Node( | |||||
| package = "hexapod_python", | |||||
| executable = "taranis_test", | |||||
| output = "both" | |||||
| ) | |||||
| joint_state_broadcaster_spawner = Node ( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["joint_state_broadcaster", "--controller-manager", "/controller_manager"] | |||||
| ) | |||||
| joint_trajectory_controller_spawner = Node( | |||||
| package = "controller_manager", | |||||
| executable = "spawner", | |||||
| arguments = ["joint_group_controller", "--controller-manager", "/controller_manager"] | |||||
| ) | |||||
| delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler( | |||||
| event_handler = OnProcessExit( | |||||
| target_action = joint_state_broadcaster_spawner, | |||||
| on_exit = [joint_trajectory_controller_spawner] | |||||
| ) | |||||
| ) | |||||
| nodes = [ | |||||
| control_node, | |||||
| taranis_node, | |||||
| ik_node, | |||||
| robot_state_publisher_node, | |||||
| joint_state_broadcaster_spawner, | |||||
| delay_joint_trajectory_controller_spawner_after_joint_state_broadcaster_spawner | |||||
| ] | |||||
| return LaunchDescription(nodes) | |||||
| @@ -0,0 +1,20 @@ | |||||
| <?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>hexapod_robot</name> | |||||
| <version>0.9.0</version> | |||||
| <description>ROS2 Robot Description and configuration</description> | |||||
| <maintainer email="marcus@grieger.xyz">Marcus Grieger</maintainer> | |||||
| <license>MIT</license> | |||||
| <buildtool_depend>ament_cmake</buildtool_depend> | |||||
| <exec_depend>hexapod_hardware</exec_depend> | |||||
| <test_depend>ament_lint_auto</test_depend> | |||||
| <test_depend>ament_lint_common</test_depend> | |||||
| <export> | |||||
| <build_type>ament_cmake</build_type> | |||||
| </export> | |||||
| </package> | |||||
| @@ -0,0 +1,49 @@ | |||||
| cmake_minimum_required(VERSION 3.8) | |||||
| project(taranis) | |||||
| 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(rosidl_default_generators REQUIRED) | |||||
| # Build Taranis interfaces | |||||
| set(msg_files | |||||
| "msg/TaranisPacket.msg" | |||||
| ) | |||||
| rosidl_generate_interfaces(${PROJECT_NAME} | |||||
| ${msg_files} | |||||
| ) | |||||
| # Build Taranis publisher node | |||||
| add_executable(taranis_node src/taranis_node.cpp) | |||||
| ament_target_dependencies(taranis_node rclcpp) | |||||
| rosidl_target_interfaces(taranis_node ${PROJECT_NAME} "rosidl_typesupport_cpp") | |||||
| target_include_directories(taranis_node PUBLIC | |||||
| $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> | |||||
| $<INSTALL_INTERFACE:include>) | |||||
| target_compile_features(taranis_node PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17 | |||||
| install(TARGETS taranis_node | |||||
| DESTINATION lib/${PROJECT_NAME}) | |||||
| if(BUILD_TESTING) | |||||
| find_package(ament_lint_auto REQUIRED) | |||||
| # the following line skips the linter which checks for copyrights | |||||
| # comment the line when a copyright and license is added to all source files | |||||
| set(ament_cmake_copyright_FOUND TRUE) | |||||
| # the following line skips cpplint (only works in a git repo) | |||||
| # comment the line when this package is in a git repo and when | |||||
| # a copyright and license is added to all source files | |||||
| set(ament_cmake_cpplint_FOUND TRUE) | |||||
| ament_lint_auto_find_test_dependencies() | |||||
| endif() | |||||
| ament_package() | |||||
| @@ -0,0 +1,17 @@ | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in | |||||
| all copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||||
| THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
| THE SOFTWARE. | |||||
| @@ -0,0 +1,44 @@ | |||||
| #ifndef TARANIS_NODE_HPP_ | |||||
| #define TARANIS_NODE_HPP_ | |||||
| #include "rclcpp/rclcpp.hpp" | |||||
| #include "taranis/msg/taranis_packet.hpp" | |||||
| #include <asm/termbits.h> | |||||
| #include <fcntl.h> | |||||
| #include <sys/ioctl.h> | |||||
| #include <sys/types.h> | |||||
| #include <unistd.h> | |||||
| #include <stdio.h> | |||||
| namespace taranis { | |||||
| class TaranisNode : public rclcpp::Node { | |||||
| public: | |||||
| TaranisNode(); | |||||
| private: | |||||
| int serial_fd_; | |||||
| termios2 termios2_; | |||||
| bool initialize_serial_(); | |||||
| rclcpp::Publisher<taranis::msg::TaranisPacket>::SharedPtr publisher_; | |||||
| rclcpp::TimerBase::SharedPtr timer_; | |||||
| void timer_callback_(); | |||||
| void publish_packet_(); | |||||
| int empty_read_ = 0; | |||||
| uint8_t buffer_index_ = 0; | |||||
| uint8_t buffer_[25]; | |||||
| double normalize_channel_(uint16_t); | |||||
| uint8_t decode_switch_(uint16_t); | |||||
| }; | |||||
| } // namespace | |||||
| #endif // TARANIS_NODE_HPP_ | |||||
| @@ -0,0 +1,15 @@ | |||||
| float64 aileron | |||||
| float64 elevator | |||||
| float64 throttle | |||||
| float64 rudder | |||||
| float64 s1 | |||||
| float64 s2 | |||||
| int8 se | |||||
| int8 sf | |||||
| int8 sg | |||||
| int8 sh | |||||
| bool failsafe_active | |||||
| bool signal_loss | |||||
| @@ -0,0 +1,25 @@ | |||||
| <?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>taranis</name> | |||||
| <version>0.0.0</version> | |||||
| <description>TODO: Package description</description> | |||||
| <maintainer email="marcus@grieger.xyz">Marcus Grieger</maintainer> | |||||
| <license>MIT</license> | |||||
| <depend>rclcpp</depend> | |||||
| <buildtool_depend>ament_cmake</buildtool_depend> | |||||
| <buildtool_depend>rosidl_default_generators</buildtool_depend> | |||||
| <exec_depend>rosidl_default_runtime</exec_depend> | |||||
| <member_of_group>rosidl_interface_packages</member_of_group> | |||||
| <test_depend>ament_lint_auto</test_depend> | |||||
| <test_depend>ament_lint_common</test_depend> | |||||
| <export> | |||||
| <build_type>ament_cmake</build_type> | |||||
| </export> | |||||
| </package> | |||||
| @@ -0,0 +1,173 @@ | |||||
| #include "taranis/taranis_node.hpp" | |||||
| #include "rclcpp/rclcpp.hpp" | |||||
| #include <functional> | |||||
| #include <memory> | |||||
| #include <chrono> | |||||
| using namespace std::chrono_literals; | |||||
| namespace taranis { | |||||
| TaranisNode::TaranisNode() : Node("taranis_node") { | |||||
| publisher_ = this->create_publisher<taranis::msg::TaranisPacket>("/taranis_output", 10); | |||||
| if(!initialize_serial_()) { | |||||
| RCLCPP_ERROR(rclcpp::get_logger("TaranisNode"), "serial could not be initialized, exiting."); | |||||
| return; | |||||
| } else { | |||||
| RCLCPP_INFO(rclcpp::get_logger("TaranisNode"), "serial port initialized successfully."); | |||||
| } | |||||
| timer_ = this->create_wall_timer( | |||||
| 2ms, | |||||
| std::bind(&TaranisNode::timer_callback_, this) | |||||
| ); | |||||
| } | |||||
| bool | |||||
| TaranisNode::initialize_serial_() { | |||||
| serial_fd_ = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NONBLOCK ); | |||||
| if (serial_fd_ < 0) { | |||||
| RCLCPP_WARN(rclcpp::get_logger("TaranisNode"), "cannot open serial."); | |||||
| return false; | |||||
| } | |||||
| if(ioctl(serial_fd_, TCGETS2, &termios2_) < 0) { | |||||
| RCLCPP_WARN(rclcpp::get_logger("TaranisNode"), "ioctl TCGETS2 failed."); | |||||
| return false; | |||||
| } | |||||
| // Set up 8-O-2 at 100000 baud, no flow-control, no pi-pa-po | |||||
| termios2_.c_iflag &= ~(INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); | |||||
| termios2_.c_iflag |= IGNBRK | IGNPAR; | |||||
| termios2_.c_oflag &= ~(OPOST | ONLCR | OCRNL | ONOCR | ONLRET | OFILL | NLDLY | VTDLY); | |||||
| termios2_.c_oflag |= NL0 | VT0; | |||||
| termios2_.c_cflag &= ~(CSIZE | CBAUD | (CBAUD << IBSHIFT)); | |||||
| termios2_.c_cflag |= CS8 | CSTOPB | PARENB | PARODD | CREAD | CLOCAL | BOTHER | (BOTHER << IBSHIFT); | |||||
| termios2_.c_lflag &= ~(ISIG | ICANON | ECHO | TOSTOP | IEXTEN); | |||||
| termios2_.c_ispeed = 100000; | |||||
| termios2_.c_ospeed = 100000; | |||||
| termios2_.c_cc[VTIME] = 0; | |||||
| termios2_.c_cc[VMIN] = 0; | |||||
| if(ioctl(serial_fd_, TCSETS2, &termios2_) < 0) { | |||||
| RCLCPP_WARN(rclcpp::get_logger("TaranisNode"), "ioctl TCSETS2 failed."); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| void | |||||
| TaranisNode::timer_callback_() { | |||||
| int bytes_read; | |||||
| bool did_read = false; | |||||
| do { | |||||
| if(empty_read_ > 2) { | |||||
| buffer_index_ = 0; | |||||
| empty_read_ = 0; | |||||
| } | |||||
| bytes_read = read(serial_fd_, &buffer_[buffer_index_], 25-buffer_index_); | |||||
| if(bytes_read) { | |||||
| buffer_index_ += bytes_read; | |||||
| did_read = true; | |||||
| if(buffer_index_ == 25) { | |||||
| publish_packet_(); | |||||
| buffer_index_ = 0; | |||||
| } | |||||
| } else { | |||||
| if(!did_read) empty_read_ ++; | |||||
| } | |||||
| } | |||||
| while(bytes_read); | |||||
| } | |||||
| double | |||||
| TaranisNode::normalize_channel_(uint16_t value) { | |||||
| double float_value = value; | |||||
| float_value -= 992.0; // (shift center to 0.0) | |||||
| float_value /= 820.0; | |||||
| if(float_value > 1.0) float_value = 1.0; | |||||
| if(float_value < -1.0) float_value = -1.0; | |||||
| return float_value; | |||||
| } | |||||
| uint8_t | |||||
| TaranisNode::decode_switch_(uint16_t value) { | |||||
| if(value < 582) return 0; | |||||
| if(value < 1402) return 1; | |||||
| return 2; | |||||
| } | |||||
| void | |||||
| TaranisNode::publish_packet_() { | |||||
| if(buffer_[0] != 0x0f || buffer_[24] != 0x00) { | |||||
| RCLCPP_WARN(rclcpp::get_logger("TaranisNode"), "invalid sbus frame received, dropping."); | |||||
| return; | |||||
| } | |||||
| uint16_t channels[16]; | |||||
| channels[ 0] = (buffer_[ 2] & 0b00000111) << 8 | buffer_[ 1]; | |||||
| channels[ 1] = (buffer_[ 3] & 0b00111111) << 5 | (buffer_[ 2] & 0b11111000) >> 3; | |||||
| channels[ 2] = (buffer_[ 5] & 0b00000001) << 10 | (buffer_[ 4] & 0b11111111) << 2 | (buffer_[3] & 0b11000000) >> 6; | |||||
| channels[ 3] = (buffer_[ 6] & 0b00001111) << 7 | (buffer_[ 5] & 0b11111110) >> 1; | |||||
| channels[ 4] = (buffer_[ 7] & 0b01111111) << 4 | (buffer_[ 6] & 0b11110000) >> 4; | |||||
| channels[ 5] = (buffer_[ 9] & 0b00000011) << 9 | (buffer_[ 8] & 0b11111111) << 1 | (buffer_[7] & 0b10000000) >> 7; | |||||
| channels[ 6] = (buffer_[10] & 0b00011111) << 6 | (buffer_[ 9] & 0b11111100) >> 2; | |||||
| channels[ 7] = (buffer_[11] & 0b11111111) << 3 | (buffer_[10] & 0b11100000) >> 5; | |||||
| channels[ 8] = (buffer_[13] & 0b00000111) << 8 | buffer_[12]; | |||||
| channels[ 9] = (buffer_[14] & 0b00111111) << 5 | (buffer_[13] & 0b11111000) >> 3; | |||||
| channels[10] = (buffer_[16] & 0b00000001) << 10 | (buffer_[15] & 0b11111111) << 2 | (buffer_[14] & 0b11000000) >> 6; | |||||
| channels[11] = (buffer_[17] & 0b00001111) << 7 | (buffer_[16] & 0b11111110) >> 1; | |||||
| channels[12] = (buffer_[18] & 0b01111111) << 4 | (buffer_[17] & 0b11110000) >> 4; | |||||
| channels[13] = (buffer_[20] & 0b00000011) << 9 | (buffer_[19] & 0b11111111) << 1 | (buffer_[18] & 0b10000000) >> 7; | |||||
| channels[14] = (buffer_[21] & 0b00011111) << 6 | (buffer_[20] & 0b11111100) >> 2; | |||||
| channels[15] = (buffer_[22] & 0b11111111) << 3 | (buffer_[21] & 0b11100000) >> 5; | |||||
| taranis::msg::TaranisPacket message = taranis::msg::TaranisPacket(); | |||||
| message.aileron = normalize_channel_(channels[0]); | |||||
| message.elevator = normalize_channel_(channels[1]); | |||||
| message.throttle = normalize_channel_(channels[2]); | |||||
| message.rudder = normalize_channel_(channels[3]); | |||||
| message.se = decode_switch_(channels[4]); | |||||
| message.sf = decode_switch_(channels[5]); | |||||
| message.sg = decode_switch_(channels[6]); | |||||
| message.sh = decode_switch_(channels[7]); | |||||
| message.s1 = normalize_channel_(channels[8]); | |||||
| message.s2 = normalize_channel_(channels[9]); | |||||
| if(buffer_[23] & (1<<2)) message.signal_loss = true; | |||||
| if(buffer_[23] & (1<<3)) message.failsafe_active = true; | |||||
| publisher_->publish(message); | |||||
| } | |||||
| } // namespace | |||||
| int main(int argc, char ** argv) | |||||
| { | |||||
| rclcpp::init(argc, argv); | |||||
| rclcpp::spin(std::make_shared<taranis::TaranisNode>()); | |||||
| rclcpp::shutdown(); | |||||
| return 0; | |||||
| } | |||||