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