init
This commit is contained in:
commit
0c69e889fb
96
CMakeLists.txt
Normal file
96
CMakeLists.txt
Normal file
@ -0,0 +1,96 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(cyphal_hardware_interface LANGUAGES CXX)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
add_compile_options(-Wall -Wextra)
|
||||
endif()
|
||||
|
||||
# find dependencies
|
||||
set(THIS_PACKAGE_INCLUDE_DEPENDS
|
||||
hardware_interface
|
||||
pluginlib
|
||||
rclcpp
|
||||
rclcpp_lifecycle
|
||||
)
|
||||
|
||||
# find dependencies
|
||||
find_package(ament_cmake REQUIRED)
|
||||
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
|
||||
find_package(${Dependency} REQUIRED)
|
||||
endforeach()
|
||||
|
||||
find_package(simplesync)
|
||||
# message(${simplesync_INCLUDE_DIR})
|
||||
|
||||
## COMPILE
|
||||
add_library(
|
||||
cyphal_hardware_interface
|
||||
SHARED
|
||||
hardware/simplesync_system.cpp
|
||||
)
|
||||
|
||||
function(get_all_targets var)
|
||||
set(targets)
|
||||
get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(${var} ${targets} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
macro(get_all_targets_recursive targets dir)
|
||||
get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
|
||||
foreach(subdir ${subdirectories})
|
||||
get_all_targets_recursive(${targets} ${subdir})
|
||||
endforeach()
|
||||
|
||||
get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS)
|
||||
list(APPEND ${targets} ${current_targets})
|
||||
endmacro()
|
||||
|
||||
target_compile_features(cyphal_hardware_interface PUBLIC cxx_std_17)
|
||||
target_include_directories(cyphal_hardware_interface PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/hardware/include>
|
||||
$<INSTALL_INTERFACE:include/cyphal_hardware_interface>
|
||||
${simplesync_INCLUDE_DIR}
|
||||
)
|
||||
ament_target_dependencies(
|
||||
cyphal_hardware_interface PUBLIC
|
||||
${THIS_PACKAGE_INCLUDE_DEPENDS}
|
||||
)
|
||||
|
||||
# Causes the visibility macros to use dllexport rather than dllimport,
|
||||
# which is appropriate when building the dll but not consuming it.
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "CYPHAL_HARDWARE_INTERFACE_BUILDING_DLL")
|
||||
|
||||
# Export hardware plugins
|
||||
pluginlib_export_plugin_description_file(hardware_interface cyphal_hardware_interface.xml)
|
||||
|
||||
# INSTALL
|
||||
install(
|
||||
DIRECTORY hardware/include/
|
||||
DESTINATION include/cyphal_hardware_interface
|
||||
)
|
||||
install(
|
||||
DIRECTORY description/launch description/ros2_control description/urdf
|
||||
DESTINATION share/cyphal_hardware_interface
|
||||
)
|
||||
install(
|
||||
DIRECTORY bringup/launch bringup/config
|
||||
DESTINATION share/cyphal_hardware_interface
|
||||
)
|
||||
install(TARGETS cyphal_hardware_interface
|
||||
EXPORT export_cyphal_hardware_interface
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_pytest REQUIRED)
|
||||
|
||||
ament_add_pytest_test(example_2_urdf_xacro test/test_urdf_xacro.py)
|
||||
ament_add_pytest_test(view_example_2_launch test/test_view_robot_launch.py)
|
||||
endif()
|
||||
|
||||
## EXPORTS
|
||||
ament_export_targets(export_cyphal_hardware_interface HAS_LIBRARY_TARGET)
|
||||
ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
|
||||
ament_package()
|
12
README.md
Normal file
12
README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# cyphal_hardware_interface
|
||||
|
||||
|
||||
<!-- ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -p stamped:=true --remap cmd_vel:=staubi_base_controller/cmd_vel -->
|
||||
<!-- ros2 launch teleop_twist_joy teleop-launch.py joy_vel:=staubi_base_controller/cmd_vel -->
|
||||
<!-- ros2 topic pub /vacuum_controller/commands std_msgs/msg/Float64MultiArray "data: [1]" -->
|
||||
|
||||
# Joystick in podman
|
||||
```
|
||||
sudo podman run -it --network host --volume="/home/nils/development/ros2_ws:/ros2_ws" --privileged osrf/ros:rolling-desktop-full bash -c "ros2 run joy joy_node"
|
||||
```
|
||||
|
77
bringup/config/staubi_controllers.yaml
Normal file
77
bringup/config/staubi_controllers.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
controller_manager:
|
||||
ros__parameters:
|
||||
update_rate: 60 # Hz
|
||||
|
||||
joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
|
||||
staubi_base_controller:
|
||||
type: diff_drive_controller/DiffDriveController
|
||||
|
||||
vacuum_controller:
|
||||
# type: forward_command_controller/ForwardCommandController
|
||||
type: joy_forward_controller/JoyForwardController
|
||||
|
||||
vacuum_controller:
|
||||
ros__parameters:
|
||||
joints:
|
||||
- vacuum
|
||||
interface_name: power
|
||||
button_number: 9
|
||||
button_toggle: true
|
||||
|
||||
staubi_base_controller:
|
||||
ros__parameters:
|
||||
left_wheel_names: ["left_wheel_joint"]
|
||||
right_wheel_names: ["right_wheel_joint"]
|
||||
|
||||
use_stamped_vel: false
|
||||
|
||||
wheel_separation: 0.222
|
||||
#wheels_per_side: 1 # actually 2, but both are controlled by 1 signal
|
||||
wheel_radius: 0.0355
|
||||
|
||||
wheel_separation_multiplier: 1.0
|
||||
left_wheel_radius_multiplier: 1.0
|
||||
right_wheel_radius_multiplier: 1.0
|
||||
|
||||
publish_rate: 60.0
|
||||
odom_frame_id: odom
|
||||
base_frame_id: base_link
|
||||
pose_covariance_diagonal : [0.001, 0.001, 0.001, 0.001, 0.001, 0.01]
|
||||
twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.01]
|
||||
|
||||
open_loop: false
|
||||
# open_loop: true
|
||||
position_feedback: true
|
||||
enable_odom_tf: true
|
||||
|
||||
cmd_vel_timeout: 0.5
|
||||
#publish_limited_velocity: true
|
||||
#velocity_rolling_window_size: 10
|
||||
|
||||
# Velocity and acceleration limits
|
||||
# Whenever a min_* is unspecified, default to -max_*
|
||||
linear.x.has_velocity_limits: true
|
||||
# linear.x.has_velocity_limits: false
|
||||
linear.x.has_acceleration_limits: true
|
||||
# linear.x.has_acceleration_limits: false
|
||||
# linear.x.has_jerk_limits: true
|
||||
linear.x.has_jerk_limits: false
|
||||
linear.x.max_velocity: 1.3
|
||||
linear.x.min_velocity: -0.3
|
||||
# linear.x.max_acceleration: 10.0
|
||||
linear.x.max_acceleration: 1.4
|
||||
# linear.x.max_acceleration: 10.0
|
||||
# linear.x.max_jerk: 1.0
|
||||
# linear.x.min_jerk: 0.0
|
||||
|
||||
angular.z.has_velocity_limits: true
|
||||
angular.z.has_acceleration_limits: true
|
||||
angular.z.has_jerk_limits: false
|
||||
angular.z.max_velocity: 7.0
|
||||
angular.z.min_velocity: -7.0
|
||||
angular.z.max_acceleration: 3.0
|
||||
angular.z.min_acceleration: -3.0
|
||||
angular.z.max_jerk: 0.0
|
||||
angular.z.min_jerk: 0.0
|
445
bringup/config/staubi_nav2.yaml
Normal file
445
bringup/config/staubi_nav2.yaml
Normal file
@ -0,0 +1,445 @@
|
||||
amcl:
|
||||
ros__parameters:
|
||||
alpha1: 0.2
|
||||
alpha2: 0.2
|
||||
alpha3: 0.2
|
||||
alpha4: 0.2
|
||||
alpha5: 0.2
|
||||
base_frame_id: "base_link"
|
||||
beam_skip_distance: 0.5
|
||||
beam_skip_error_threshold: 0.9
|
||||
beam_skip_threshold: 0.3
|
||||
do_beamskip: false
|
||||
global_frame_id: "map"
|
||||
lambda_short: 0.1
|
||||
laser_likelihood_max_dist: 2.0
|
||||
laser_max_range: 100.0
|
||||
laser_min_range: -1.0
|
||||
laser_model_type: "likelihood_field"
|
||||
max_beams: 60
|
||||
max_particles: 2000
|
||||
min_particles: 500
|
||||
odom_frame_id: "odom"
|
||||
pf_err: 0.05
|
||||
pf_z: 0.99
|
||||
recovery_alpha_fast: 0.0
|
||||
recovery_alpha_slow: 0.0
|
||||
resample_interval: 1
|
||||
robot_model_type: "nav2_amcl::DifferentialMotionModel"
|
||||
save_pose_rate: 0.5
|
||||
sigma_hit: 0.2
|
||||
tf_broadcast: true
|
||||
transform_tolerance: 1.0
|
||||
update_min_a: 0.2
|
||||
update_min_d: 0.25
|
||||
z_hit: 0.5
|
||||
z_max: 0.05
|
||||
z_rand: 0.5
|
||||
z_short: 0.05
|
||||
scan_topic: scan
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
bt_navigator:
|
||||
ros__parameters:
|
||||
global_frame: map
|
||||
robot_base_frame: base_link
|
||||
odom_topic: /odom
|
||||
bt_loop_duration: 10
|
||||
default_server_timeout: 20
|
||||
wait_for_service_timeout: 1000
|
||||
action_server_result_timeout: 900.0
|
||||
navigators: ["navigate_to_pose", "navigate_through_poses"]
|
||||
navigate_to_pose:
|
||||
plugin: "nav2_bt_navigator::NavigateToPoseNavigator"
|
||||
navigate_through_poses:
|
||||
plugin: "nav2_bt_navigator::NavigateThroughPosesNavigator"
|
||||
# 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
|
||||
# nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
|
||||
# nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
|
||||
# They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
|
||||
|
||||
# plugin_lib_names is used to add custom BT plugins to the executor (vector of strings).
|
||||
# Built-in plugins are added automatically
|
||||
# plugin_lib_names: []
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
error_code_names:
|
||||
- compute_path_error_code
|
||||
- follow_path_error_code
|
||||
|
||||
controller_server:
|
||||
ros__parameters:
|
||||
controller_frequency: 20.0
|
||||
min_x_velocity_threshold: 0.001
|
||||
min_y_velocity_threshold: 0.5
|
||||
min_theta_velocity_threshold: 0.001
|
||||
failure_tolerance: 0.3
|
||||
progress_checker_plugins: ["progress_checker"]
|
||||
goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
|
||||
controller_plugins: ["FollowPath"]
|
||||
use_realtime_priority: false
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
# Progress checker parameters
|
||||
progress_checker:
|
||||
plugin: "nav2_controller::SimpleProgressChecker"
|
||||
required_movement_radius: 0.5
|
||||
movement_time_allowance: 10.0
|
||||
# Goal checker parameters
|
||||
#precise_goal_checker:
|
||||
# plugin: "nav2_controller::SimpleGoalChecker"
|
||||
# xy_goal_tolerance: 0.25
|
||||
# yaw_goal_tolerance: 0.25
|
||||
# stateful: True
|
||||
general_goal_checker:
|
||||
stateful: True
|
||||
plugin: "nav2_controller::SimpleGoalChecker"
|
||||
xy_goal_tolerance: 0.25
|
||||
yaw_goal_tolerance: 0.25
|
||||
FollowPath:
|
||||
plugin: "nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController"
|
||||
desired_linear_vel: 0.5
|
||||
lookahead_dist: 0.6
|
||||
min_lookahead_dist: 0.3
|
||||
max_lookahead_dist: 0.9
|
||||
lookahead_time: 1.5
|
||||
rotate_to_heading_angular_vel: 1.8
|
||||
transform_tolerance: 0.1
|
||||
use_velocity_scaled_lookahead_dist: false
|
||||
min_approach_linear_velocity: 0.05
|
||||
approach_velocity_scaling_dist: 0.6
|
||||
use_collision_detection: true
|
||||
max_allowed_time_to_collision_up_to_carrot: 1.0
|
||||
use_regulated_linear_velocity_scaling: true
|
||||
use_fixed_curvature_lookahead: false
|
||||
curvature_lookahead_dist: 0.25
|
||||
use_cost_regulated_linear_velocity_scaling: false
|
||||
regulated_linear_scaling_min_radius: 0.9
|
||||
regulated_linear_scaling_min_speed: 0.25
|
||||
use_rotate_to_heading: true
|
||||
allow_reversing: false
|
||||
rotate_to_heading_min_angle: 0.785
|
||||
max_angular_accel: 3.2
|
||||
max_robot_pose_search_dist: 10.0
|
||||
|
||||
# plugin: "nav2_mppi_controller::MPPIController"
|
||||
# time_steps: 56
|
||||
# model_dt: 0.05
|
||||
# batch_size: 2000
|
||||
# vx_std: 0.2
|
||||
# vy_std: 0.2
|
||||
# wz_std: 0.4
|
||||
# vx_max: 0.5
|
||||
# vx_min: -0.35
|
||||
# vy_max: 0.5
|
||||
# wz_max: 1.9
|
||||
# iteration_count: 1
|
||||
# prune_distance: 1.7
|
||||
# transform_tolerance: 0.1
|
||||
# temperature: 0.3
|
||||
# gamma: 0.015
|
||||
# motion_model: "DiffDrive"
|
||||
# visualize: true
|
||||
# regenerate_noises: true
|
||||
# TrajectoryVisualizer:
|
||||
# trajectory_step: 5
|
||||
# time_step: 3
|
||||
# AckermannConstraints:
|
||||
# min_turning_r: 0.2
|
||||
# critics: [
|
||||
# "ConstraintCritic", "CostCritic", "GoalCritic",
|
||||
# "GoalAngleCritic", "PathAlignCritic", "PathFollowCritic",
|
||||
# "PathAngleCritic", "PreferForwardCritic"]
|
||||
# ConstraintCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 4.0
|
||||
# GoalCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 5.0
|
||||
# threshold_to_consider: 1.4
|
||||
# GoalAngleCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 3.0
|
||||
# threshold_to_consider: 0.5
|
||||
# PreferForwardCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 5.0
|
||||
# threshold_to_consider: 0.5
|
||||
# CostCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 3.81
|
||||
# critical_cost: 300.0
|
||||
# consider_footprint: true
|
||||
# collision_cost: 1000000.0
|
||||
# near_goal_distance: 1.0
|
||||
# trajectory_point_step: 2
|
||||
# PathAlignCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 14.0
|
||||
# max_path_occupancy_ratio: 0.05
|
||||
# trajectory_point_step: 4
|
||||
# threshold_to_consider: 0.5
|
||||
# offset_from_furthest: 20
|
||||
# use_path_orientations: false
|
||||
# PathFollowCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 5.0
|
||||
# offset_from_furthest: 5
|
||||
# threshold_to_consider: 1.4
|
||||
# PathAngleCritic:
|
||||
# enabled: true
|
||||
# cost_power: 1
|
||||
# cost_weight: 2.0
|
||||
# offset_from_furthest: 4
|
||||
# threshold_to_consider: 0.5
|
||||
# max_angle_to_furthest: 1.0
|
||||
# mode: 0
|
||||
|
||||
# plugin: "dwb_core::DWBLocalPlanner"
|
||||
# debug_trajectory_details: True
|
||||
# min_vel_x: 0.0
|
||||
# min_vel_y: 0.0
|
||||
# max_vel_x: 0.26
|
||||
# max_vel_y: 0.0
|
||||
# max_vel_theta: 1.0
|
||||
# min_speed_xy: 0.0
|
||||
# max_speed_xy: 0.26
|
||||
# min_speed_theta: 0.0
|
||||
# acc_lim_x: 2.5
|
||||
# acc_lim_y: 0.0
|
||||
# acc_lim_theta: 3.2
|
||||
# decel_lim_x: -2.5
|
||||
# decel_lim_y: 0.0
|
||||
# decel_lim_theta: -3.2
|
||||
# vx_samples: 20
|
||||
# vy_samples: 5
|
||||
# vtheta_samples: 20
|
||||
# sim_time: 1.7
|
||||
# linear_granularity: 0.05
|
||||
# angular_granularity: 0.025
|
||||
# transform_tolerance: 0.2
|
||||
# xy_goal_tolerance: 0.25
|
||||
# trans_stopped_velocity: 0.25
|
||||
# short_circuit_trajectory_evaluation: True
|
||||
# stateful: True
|
||||
# critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
|
||||
# BaseObstacle.scale: 0.02
|
||||
# PathAlign.scale: 32.0
|
||||
# GoalAlign.scale: 24.0
|
||||
# PathAlign.forward_point_distance: 0.1
|
||||
# GoalAlign.forward_point_distance: 0.1
|
||||
# PathDist.scale: 32.0
|
||||
# GoalDist.scale: 24.0
|
||||
# RotateToGoal.scale: 32.0
|
||||
# RotateToGoal.slowing_factor: 5.0
|
||||
# RotateToGoal.lookahead_time: -1.0
|
||||
# TwirlingCritic:
|
||||
# enabled: true
|
||||
# twirling_cost_power: 1
|
||||
# twirling_cost_weight: 10.0
|
||||
|
||||
local_costmap:
|
||||
local_costmap:
|
||||
ros__parameters:
|
||||
update_frequency: 5.0
|
||||
publish_frequency: 2.0
|
||||
global_frame: odom
|
||||
robot_base_frame: base_link
|
||||
rolling_window: true
|
||||
width: 3
|
||||
height: 3
|
||||
resolution: 0.05
|
||||
robot_radius: 0.22
|
||||
plugins: ["voxel_layer", "inflation_layer"]
|
||||
inflation_layer:
|
||||
plugin: "nav2_costmap_2d::InflationLayer"
|
||||
cost_scaling_factor: 3.0
|
||||
inflation_radius: 0.70
|
||||
voxel_layer:
|
||||
plugin: "nav2_costmap_2d::VoxelLayer"
|
||||
enabled: True
|
||||
publish_voxel_map: True
|
||||
origin_z: 0.0
|
||||
z_resolution: 0.05
|
||||
z_voxels: 16
|
||||
max_obstacle_height: 2.0
|
||||
mark_threshold: 0
|
||||
observation_sources: scan
|
||||
scan:
|
||||
topic: /scan
|
||||
max_obstacle_height: 2.0
|
||||
clearing: True
|
||||
marking: True
|
||||
data_type: "LaserScan"
|
||||
raytrace_max_range: 3.0
|
||||
raytrace_min_range: 0.0
|
||||
obstacle_max_range: 2.5
|
||||
obstacle_min_range: 0.0
|
||||
static_layer:
|
||||
plugin: "nav2_costmap_2d::StaticLayer"
|
||||
map_subscribe_transient_local: True
|
||||
always_send_full_costmap: True
|
||||
|
||||
global_costmap:
|
||||
global_costmap:
|
||||
ros__parameters:
|
||||
update_frequency: 1.0
|
||||
publish_frequency: 1.0
|
||||
global_frame: map
|
||||
robot_base_frame: base_link
|
||||
robot_radius: 0.22
|
||||
resolution: 0.05
|
||||
track_unknown_space: true
|
||||
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
|
||||
obstacle_layer:
|
||||
plugin: "nav2_costmap_2d::ObstacleLayer"
|
||||
enabled: True
|
||||
observation_sources: scan
|
||||
scan:
|
||||
topic: /scan
|
||||
max_obstacle_height: 2.0
|
||||
clearing: True
|
||||
marking: True
|
||||
data_type: "LaserScan"
|
||||
raytrace_max_range: 3.0
|
||||
raytrace_min_range: 0.0
|
||||
obstacle_max_range: 2.5
|
||||
obstacle_min_range: 0.0
|
||||
static_layer:
|
||||
plugin: "nav2_costmap_2d::StaticLayer"
|
||||
map_subscribe_transient_local: True
|
||||
inflation_layer:
|
||||
plugin: "nav2_costmap_2d::InflationLayer"
|
||||
cost_scaling_factor: 3.0
|
||||
inflation_radius: 0.7
|
||||
always_send_full_costmap: True
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
# The yaml_filename does not need to be specified since it going to be set by defaults in launch.
|
||||
# If you'd rather set it in the yaml, remove the default "map" value in the tb3_simulation_launch.py
|
||||
# file & provide full path to map below. If CLI map configuration or launch default is provided, that will be used.
|
||||
# map_server:
|
||||
# ros__parameters:
|
||||
# yaml_filename: ""
|
||||
|
||||
map_saver:
|
||||
ros__parameters:
|
||||
save_map_timeout: 5.0
|
||||
free_thresh_default: 0.25
|
||||
occupied_thresh_default: 0.65
|
||||
map_subscribe_transient_local: True
|
||||
|
||||
planner_server:
|
||||
ros__parameters:
|
||||
expected_planner_frequency: 20.0
|
||||
planner_plugins: ["GridBased"]
|
||||
GridBased:
|
||||
plugin: "nav2_navfn_planner::NavfnPlanner"
|
||||
tolerance: 0.5
|
||||
use_astar: false
|
||||
allow_unknown: true
|
||||
|
||||
smoother_server:
|
||||
ros__parameters:
|
||||
smoother_plugins: ["simple_smoother"]
|
||||
simple_smoother:
|
||||
plugin: "nav2_smoother::SimpleSmoother"
|
||||
tolerance: 1.0e-10
|
||||
max_its: 1000
|
||||
do_refinement: True
|
||||
enable_stamped_cmd_vel: true
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
behavior_server:
|
||||
ros__parameters:
|
||||
local_costmap_topic: local_costmap/costmap_raw
|
||||
global_costmap_topic: global_costmap/costmap_raw
|
||||
local_footprint_topic: local_costmap/published_footprint
|
||||
global_footprint_topic: global_costmap/published_footprint
|
||||
cycle_frequency: 10.0
|
||||
behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
|
||||
spin:
|
||||
plugin: "nav2_behaviors::Spin"
|
||||
backup:
|
||||
plugin: "nav2_behaviors::BackUp"
|
||||
drive_on_heading:
|
||||
plugin: "nav2_behaviors::DriveOnHeading"
|
||||
wait:
|
||||
plugin: "nav2_behaviors::Wait"
|
||||
assisted_teleop:
|
||||
plugin: "nav2_behaviors::AssistedTeleop"
|
||||
local_frame: odom
|
||||
global_frame: map
|
||||
robot_base_frame: base_link
|
||||
transform_tolerance: 0.1
|
||||
simulate_ahead_time: 2.0
|
||||
max_rotational_vel: 1.0
|
||||
min_rotational_vel: 0.4
|
||||
rotational_acc_lim: 3.2
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
waypoint_follower:
|
||||
ros__parameters:
|
||||
loop_rate: 20
|
||||
stop_on_failure: false
|
||||
action_server_result_timeout: 900.0
|
||||
waypoint_task_executor_plugin: "wait_at_waypoint"
|
||||
wait_at_waypoint:
|
||||
plugin: "nav2_waypoint_follower::WaitAtWaypoint"
|
||||
enabled: True
|
||||
waypoint_pause_duration: 200
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
velocity_smoother:
|
||||
ros__parameters:
|
||||
smoothing_frequency: 20.0
|
||||
scale_velocities: False
|
||||
feedback: "OPEN_LOOP"
|
||||
max_velocity: [0.5, 0.0, 2.0]
|
||||
min_velocity: [-0.5, 0.0, -2.0]
|
||||
max_accel: [2.5, 0.0, 3.2]
|
||||
max_decel: [-2.5, 0.0, -3.2]
|
||||
odom_topic: "odom"
|
||||
odom_duration: 0.1
|
||||
deadband_velocity: [0.0, 0.0, 0.0]
|
||||
velocity_timeout: 1.0
|
||||
enable_stamped_cmd_vel: true
|
||||
|
||||
collision_monitor:
|
||||
ros__parameters:
|
||||
base_frame_id: "base_link"
|
||||
odom_frame_id: "odom"
|
||||
cmd_vel_in_topic: "cmd_vel_smoothed"
|
||||
cmd_vel_out_topic: "cmd_vel"
|
||||
state_topic: "collision_monitor_state"
|
||||
transform_tolerance: 0.2
|
||||
source_timeout: 1.0
|
||||
base_shift_correction: True
|
||||
stop_pub_timeout: 2.0
|
||||
enable_stamped_cmd_vel: true
|
||||
# Polygons represent zone around the robot for "stop", "slowdown" and "limit" action types,
|
||||
# and robot footprint for "approach" action type.
|
||||
polygons: ["FootprintApproach"]
|
||||
FootprintApproach:
|
||||
type: "polygon"
|
||||
action_type: "approach"
|
||||
footprint_topic: "/local_costmap/published_footprint"
|
||||
time_before_collision: 1.2
|
||||
simulation_time_step: 0.1
|
||||
min_points: 6
|
||||
visualize: False
|
||||
enabled: True
|
||||
observation_sources: ["scan"]
|
||||
scan:
|
||||
type: "scan"
|
||||
topic: "scan"
|
||||
min_height: 0.15
|
||||
max_height: 2.0
|
||||
enabled: True
|
253
bringup/launch/staubi.launch.py
Normal file
253
bringup/launch/staubi.launch.py
Normal file
@ -0,0 +1,253 @@
|
||||
# Copyright 2020 ros2_control Development Team
|
||||
#
|
||||
# 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 launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, RegisterEventHandler
|
||||
from launch.conditions import IfCondition
|
||||
from launch.event_handlers import OnProcessExit
|
||||
from launch.substitutions import Command, FindExecutable, PathJoinSubstitution, LaunchConfiguration
|
||||
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
def generate_launch_description():
|
||||
# Declare arguments
|
||||
declared_arguments = []
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"gui",
|
||||
default_value="false",
|
||||
description="Start RViz2 automatically with this launch file.",
|
||||
)
|
||||
)
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"joy",
|
||||
default_value="true",
|
||||
description="Start joy node with this launch file.",
|
||||
)
|
||||
)
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"use_mock_hardware",
|
||||
default_value="false",
|
||||
description="Start robot with mock hardware mirroring command to its states.",
|
||||
)
|
||||
)
|
||||
lidar_serial_baudrate = LaunchConfiguration('serial_baudrate', default='115200')
|
||||
lidar_channel_type = LaunchConfiguration('channel_type', default='serial')
|
||||
lidar_frame_id = LaunchConfiguration('frame_id', default='laser')
|
||||
lidar_serial_port = LaunchConfiguration('serial_port', default='/dev/ttyUSB0')
|
||||
lidar_inverted = LaunchConfiguration('inverted', default='false')
|
||||
lidar_angle_compensate = LaunchConfiguration('angle_compensate', default='true')
|
||||
lidar_scan_mode = LaunchConfiguration('scan_mode', default='Standard')
|
||||
joy_dev = LaunchConfiguration('joy_dev', default='/dev/input/js0')
|
||||
|
||||
# Initialize Arguments
|
||||
gui = LaunchConfiguration("gui")
|
||||
joy = LaunchConfiguration("joy")
|
||||
use_mock_hardware = LaunchConfiguration("use_mock_hardware")
|
||||
|
||||
# Get URDF via xacro
|
||||
robot_description_content = Command(
|
||||
[
|
||||
PathJoinSubstitution([FindExecutable(name="xacro")]),
|
||||
" ",
|
||||
PathJoinSubstitution(
|
||||
[FindPackageShare("cyphal_hardware_interface"), "urdf", "staubi.urdf.xacro"]
|
||||
),
|
||||
" ",
|
||||
"use_mock_hardware:=",
|
||||
use_mock_hardware,
|
||||
]
|
||||
)
|
||||
robot_description = {"robot_description": robot_description_content}
|
||||
|
||||
robot_controllers = PathJoinSubstitution(
|
||||
[
|
||||
FindPackageShare("cyphal_hardware_interface"),
|
||||
"config",
|
||||
"staubi_controllers.yaml",
|
||||
]
|
||||
)
|
||||
rviz_config_file = PathJoinSubstitution(
|
||||
[FindPackageShare("cyphal_hardware_interface"), "staubi/rviz", "staubi.rviz"]
|
||||
)
|
||||
|
||||
|
||||
|
||||
lidar_ld = [
|
||||
DeclareLaunchArgument(
|
||||
'channel_type',
|
||||
default_value=lidar_channel_type,
|
||||
description='Specifying channel type of lidar'),
|
||||
|
||||
DeclareLaunchArgument(
|
||||
'serial_port',
|
||||
default_value=lidar_serial_port,
|
||||
description='Specifying usb port to connected lidar'),
|
||||
|
||||
DeclareLaunchArgument(
|
||||
'serial_baudrate',
|
||||
default_value=lidar_serial_baudrate,
|
||||
description='Specifying usb port baudrate to connected lidar'),
|
||||
|
||||
DeclareLaunchArgument(
|
||||
'frame_id',
|
||||
default_value=lidar_frame_id,
|
||||
description='Specifying frame_id of lidar'),
|
||||
|
||||
DeclareLaunchArgument(
|
||||
'inverted',
|
||||
default_value=lidar_inverted,
|
||||
description='Specifying whether or not to invert scan data'),
|
||||
|
||||
DeclareLaunchArgument(
|
||||
'angle_compensate',
|
||||
default_value=lidar_angle_compensate,
|
||||
description='Specifying whether or not to enable angle_compensate of scan data'),
|
||||
DeclareLaunchArgument(
|
||||
'scan_mode',
|
||||
default_value=lidar_scan_mode,
|
||||
description='Specifying scan mode of lidar'),
|
||||
|
||||
Node(
|
||||
package='rplidar_ros',
|
||||
executable='rplidar_node',
|
||||
name='rplidar_node',
|
||||
parameters=[{'channel_type':lidar_channel_type,
|
||||
'serial_port': lidar_serial_port,
|
||||
'serial_baudrate': lidar_serial_baudrate,
|
||||
'frame_id': lidar_frame_id,
|
||||
'inverted': lidar_inverted,
|
||||
'angle_compensate': lidar_angle_compensate,
|
||||
'scan_mode': lidar_scan_mode
|
||||
}],
|
||||
output='screen'),]
|
||||
|
||||
control_node = Node(
|
||||
package="controller_manager",
|
||||
executable="ros2_control_node",
|
||||
parameters=[robot_description, robot_controllers],
|
||||
output="both",
|
||||
remappings=[
|
||||
("/vacuum_controller/joy", "/joy"),
|
||||
("/staubi_base_controller/cmd_vel", "/cmd_vel_nav"),
|
||||
],
|
||||
)
|
||||
robot_state_pub_node = Node(
|
||||
package="robot_state_publisher",
|
||||
executable="robot_state_publisher",
|
||||
output="both",
|
||||
parameters=[robot_description],
|
||||
# remappings=[
|
||||
# ("/diff_drive_controller/cmd_vel_unstamped", "/cmd_vel"),
|
||||
# ],
|
||||
)
|
||||
rviz_node = Node(
|
||||
package="rviz2",
|
||||
executable="rviz2",
|
||||
name="rviz2",
|
||||
output="log",
|
||||
arguments=["-d", rviz_config_file],
|
||||
condition=IfCondition(gui),
|
||||
)
|
||||
|
||||
joint_state_broadcaster_spawner = Node(
|
||||
package="controller_manager",
|
||||
executable="spawner",
|
||||
arguments=["joint_state_broadcaster", "--controller-manager", "/controller_manager"],
|
||||
)
|
||||
|
||||
robot_controller_spawner = Node(
|
||||
package="controller_manager",
|
||||
executable="spawner",
|
||||
arguments=["staubi_base_controller", "--controller-manager", "/controller_manager"],
|
||||
)
|
||||
|
||||
vacuum_controller_spawner = Node(
|
||||
package="controller_manager",
|
||||
executable="spawner",
|
||||
arguments=["vacuum_controller", "--controller-manager", "/controller_manager"],
|
||||
)
|
||||
|
||||
# Delay rviz start after `joint_state_broadcaster`
|
||||
delay_rviz_after_joint_state_broadcaster_spawner = RegisterEventHandler(
|
||||
event_handler=OnProcessExit(
|
||||
target_action=joint_state_broadcaster_spawner,
|
||||
on_exit=[rviz_node],
|
||||
)
|
||||
)
|
||||
|
||||
# Delay start of robot_controller after `joint_state_broadcaster`
|
||||
delay_robot_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler(
|
||||
event_handler=OnProcessExit(
|
||||
target_action=joint_state_broadcaster_spawner,
|
||||
on_exit=[robot_controller_spawner],
|
||||
)
|
||||
)
|
||||
|
||||
delay_vacuum_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler(
|
||||
event_handler=OnProcessExit(
|
||||
target_action=joint_state_broadcaster_spawner,
|
||||
on_exit=[vacuum_controller_spawner],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
joy_ld = [
|
||||
DeclareLaunchArgument('joy_vel', default_value='/cmd_vel_nav'),
|
||||
DeclareLaunchArgument('joy_config', default_value='ps3'),
|
||||
DeclareLaunchArgument('joy_dev', default_value=joy_dev),
|
||||
Node(
|
||||
package='joy_linux', executable='joy_linux_node', name='joy_linux_node',
|
||||
parameters=[{
|
||||
'dev': joy_dev,
|
||||
# 'deadzone': 0.3,
|
||||
# 'autorepeat_rate': 20.0,
|
||||
}],
|
||||
condition=IfCondition(joy),
|
||||
),
|
||||
Node(
|
||||
package='teleop_twist_joy', executable='teleop_node',
|
||||
name='teleop_twist_joy_node', parameters=[ {
|
||||
"publish_stamped_twist": True,
|
||||
# "require_enable_button": False,
|
||||
"enable_button": 10,
|
||||
# "scale_linear.x":3.0,
|
||||
"axis_angular.yaw": 2,
|
||||
"axis_linear.x": 3,
|
||||
"scale_linear.x": 0.5,
|
||||
"scale_angular.yaw": 1.6,
|
||||
"scale_linear_turbo.x": 1.8,
|
||||
"scale_angular_turbo.yaw": 2.6,
|
||||
"enable_turbo_button": 7,
|
||||
"frame": "base_link",
|
||||
}],
|
||||
# condition=IfCondition(joystick),
|
||||
remappings={('/cmd_vel', LaunchConfiguration('joy_vel'))},
|
||||
),
|
||||
]
|
||||
|
||||
nodes = [
|
||||
control_node,
|
||||
robot_state_pub_node,
|
||||
joint_state_broadcaster_spawner,
|
||||
delay_rviz_after_joint_state_broadcaster_spawner,
|
||||
delay_robot_controller_spawner_after_joint_state_broadcaster_spawner,
|
||||
delay_vacuum_controller_spawner_after_joint_state_broadcaster_spawner,
|
||||
] + joy_ld + lidar_ld
|
||||
|
||||
return LaunchDescription(declared_arguments + nodes)
|
9
cyphal_hardware_interface.xml
Normal file
9
cyphal_hardware_interface.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<library path="cyphal_hardware_interface">
|
||||
<class name="cyphal_hardware_interface/cyphalSystemHardware"
|
||||
type="cyphal_hardware_interface::cyphalSystemHardware"
|
||||
base_class_type="hardware_interface::SystemInterface">
|
||||
<description>
|
||||
The ros2_control cyphal_hardware_interface using a system hardware interface-type.
|
||||
</description>
|
||||
</class>
|
||||
</library>
|
111
description/launch/view_robot.launch.py
Normal file
111
description/launch/view_robot.launch.py
Normal file
@ -0,0 +1,111 @@
|
||||
# Copyright 2021 Stogl Robotics Consulting UG (haftungsbeschränkt)
|
||||
#
|
||||
# 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 launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import IfCondition
|
||||
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
|
||||
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# Declare arguments
|
||||
declared_arguments = []
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"description_package",
|
||||
default_value="ros2_control_demo_description",
|
||||
description="Description package with robot URDF/xacro files. Usually the argument \
|
||||
is not set, it enables use of a custom description.",
|
||||
)
|
||||
)
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"description_file",
|
||||
default_value="staubi.urdf.xacro",
|
||||
description="URDF/XACRO description file with the robot.",
|
||||
)
|
||||
)
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"gui",
|
||||
default_value="true",
|
||||
description="Start Rviz2 and Joint State Publisher gui automatically \
|
||||
with this launch file.",
|
||||
)
|
||||
)
|
||||
declared_arguments.append(
|
||||
DeclareLaunchArgument(
|
||||
"prefix",
|
||||
default_value='""',
|
||||
description="Prefix of the joint names, useful for \
|
||||
multi-robot setup. If changed than also joint names in the controllers' configuration \
|
||||
have to be updated.",
|
||||
)
|
||||
)
|
||||
|
||||
# Initialize Arguments
|
||||
description_package = LaunchConfiguration("description_package")
|
||||
description_file = LaunchConfiguration("description_file")
|
||||
gui = LaunchConfiguration("gui")
|
||||
prefix = LaunchConfiguration("prefix")
|
||||
|
||||
# Get URDF via xacro
|
||||
robot_description_content = Command(
|
||||
[
|
||||
PathJoinSubstitution([FindExecutable(name="xacro")]),
|
||||
" ",
|
||||
PathJoinSubstitution(
|
||||
[FindPackageShare("cyphal_hardware_interface"), "urdf", description_file]
|
||||
),
|
||||
" ",
|
||||
"prefix:=",
|
||||
prefix,
|
||||
]
|
||||
)
|
||||
robot_description = {"robot_description": robot_description_content}
|
||||
|
||||
rviz_config_file = PathJoinSubstitution(
|
||||
[FindPackageShare(description_package), "staubi/rviz", "staubi_view.rviz"]
|
||||
)
|
||||
|
||||
joint_state_publisher_node = Node(
|
||||
package="joint_state_publisher_gui",
|
||||
executable="joint_state_publisher_gui",
|
||||
condition=IfCondition(gui),
|
||||
)
|
||||
robot_state_publisher_node = Node(
|
||||
package="robot_state_publisher",
|
||||
executable="robot_state_publisher",
|
||||
output="both",
|
||||
parameters=[robot_description],
|
||||
)
|
||||
rviz_node = Node(
|
||||
package="rviz2",
|
||||
executable="rviz2",
|
||||
name="rviz2",
|
||||
output="log",
|
||||
arguments=["-d", rviz_config_file],
|
||||
condition=IfCondition(gui),
|
||||
)
|
||||
|
||||
nodes = [
|
||||
joint_state_publisher_node,
|
||||
robot_state_publisher_node,
|
||||
rviz_node,
|
||||
]
|
||||
|
||||
return LaunchDescription(declared_arguments + nodes)
|
63
description/ros2_control/staubi.ros2_control.xacro
Normal file
63
description/ros2_control/staubi.ros2_control.xacro
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="staubi_ros2_control" params="name prefix use_mock_hardware">
|
||||
|
||||
<ros2_control name="${name}" type="system">
|
||||
<xacro:unless value="${use_mock_hardware}">
|
||||
<hardware>
|
||||
<plugin>cyphal_hardware_interface/cyphalSystemHardware</plugin>
|
||||
<param name="port">/dev/ttyAMA10</param>
|
||||
<!--param name="port">/dev/ttyACM0</param-->
|
||||
<param name="baud">460800</param>
|
||||
<!--param name="try_next_port_number">false</param-->
|
||||
</hardware>
|
||||
</xacro:unless>
|
||||
<xacro:if value="${use_mock_hardware}">
|
||||
<hardware>
|
||||
<plugin>mock_components/GenericSystem</plugin>
|
||||
<param name="calculate_dynamics">true</param>
|
||||
</hardware>
|
||||
</xacro:if>
|
||||
<sensor name="${prefix}driver">
|
||||
<param name="state:battery_voltage">vcc</param>
|
||||
<param name="factor">0.001</param>
|
||||
<state_interface name="battery_voltage"/>
|
||||
</sensor>
|
||||
<joint name="${prefix}vacuum">
|
||||
<param name="command:power">s</param>
|
||||
<param name="factor">0.1427</param>
|
||||
<command_interface name="power">
|
||||
<param name="min">0</param>
|
||||
<param name="max">1</param>
|
||||
</command_interface>
|
||||
</joint>
|
||||
<joint name="${prefix}left_wheel_joint">
|
||||
<param name="command:velocity">vt0</param>
|
||||
<param name="state:position">a0</param>
|
||||
<param name="state:velocity">v0</param>
|
||||
<param name="factor">0.0001</param>
|
||||
<command_interface name="velocity">
|
||||
</command_interface>
|
||||
<state_interface name="position">
|
||||
</state_interface>
|
||||
<!--state_interface name="velocity">
|
||||
</state_interface-->
|
||||
</joint>
|
||||
<joint name="${prefix}right_wheel_joint">
|
||||
<param name="command:velocity">vt1</param>
|
||||
<param name="state:position">a1</param>
|
||||
<param name="state:velocity">v1</param>
|
||||
<param name="factor">0.0001</param>
|
||||
<command_interface name="velocity">
|
||||
</command_interface>
|
||||
<state_interface name="position">
|
||||
</state_interface>
|
||||
<!--state_interface name="velocity">
|
||||
</state_interface-->
|
||||
</joint>
|
||||
</ros2_control>
|
||||
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
40
description/urdf/staubi.materials.xacro
Normal file
40
description/urdf/staubi.materials.xacro
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Copied from ROS1 example:
|
||||
https://github.com/ros-simulation/gazebo_ros_demos/blob/kinetic-devel/rrbot_description/urdf/materials.xacro
|
||||
-->
|
||||
<robot>
|
||||
|
||||
<material name="black">
|
||||
<color rgba="0.0 0.0 0.0 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="blue">
|
||||
<color rgba="0.0 0.0 0.8 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="green">
|
||||
<color rgba="0.0 0.8 0.0 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="grey">
|
||||
<color rgba="0.2 0.2 0.2 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="orange">
|
||||
<color rgba="${255/255} ${108/255} ${10/255} 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="brown">
|
||||
<color rgba="${222/255} ${207/255} ${195/255} 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="red">
|
||||
<color rgba="0.8 0.0 0.0 1.0"/>
|
||||
</material>
|
||||
|
||||
<material name="white">
|
||||
<color rgba="1.0 1.0 1.0 1.0"/>
|
||||
</material>
|
||||
|
||||
</robot>
|
20
description/urdf/staubi.urdf.xacro
Normal file
20
description/urdf/staubi.urdf.xacro
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Basic differential drive mobile base -->
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="staubi_robot">
|
||||
<xacro:arg name="prefix" default="" />
|
||||
<xacro:arg name="use_mock_hardware" default="false" />
|
||||
|
||||
<xacro:include filename="$(find cyphal_hardware_interface)/urdf/staubi_description.urdf.xacro" />
|
||||
|
||||
<!-- Import Rviz colors -->
|
||||
<xacro:include filename="$(find cyphal_hardware_interface)/urdf/staubi.materials.xacro" />
|
||||
|
||||
<!-- Import staubi ros2_control description -->
|
||||
<xacro:include filename="$(find cyphal_hardware_interface)/ros2_control/staubi.ros2_control.xacro" />
|
||||
|
||||
<xacro:staubi prefix="$(arg prefix)" />
|
||||
|
||||
<xacro:staubi_ros2_control
|
||||
name="Staubi" prefix="$(arg prefix)" use_mock_hardware="$(arg use_mock_hardware)"/>
|
||||
|
||||
</robot>
|
153
description/urdf/staubi_description.urdf.xacro
Normal file
153
description/urdf/staubi_description.urdf.xacro
Normal file
@ -0,0 +1,153 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="staubi" params="prefix">
|
||||
|
||||
<!-- Constants for robot dimensions -->
|
||||
<xacro:property name="PI" value="3.1415926535897931"/>
|
||||
<xacro:property name="base_mass" value="0.3" /> <!-- arbitrary value for base mass -->
|
||||
<xacro:property name="base_width" value="0.345" />
|
||||
<xacro:property name="base_height" value="0.07" />
|
||||
<xacro:property name="floor_clearance" value="0.02" />
|
||||
<xacro:property name="wheel_mass" value="0.3" /> <!-- arbitrary value for wheel mass -->
|
||||
<xacro:property name="wheel_len" value="0.015" />
|
||||
<xacro:property name="wheel_radius" value="${0.071 / 2}" />
|
||||
<xacro:property name="wheel_distance" value="${0.222}" />
|
||||
<xacro:property name="caster_wheel_mass" value="0.1" /> <!-- arbitrary value for caster wheel mass -->
|
||||
<xacro:property name="caster_wheel_radius" value="0.030" />
|
||||
<xacro:property name="z_offset" value="-${base_height/2}" /> <!-- Space btw top of beam and the each joint -->
|
||||
|
||||
<!-- Base Link -->
|
||||
<link name="${prefix}base_link">
|
||||
<collision>
|
||||
<origin xyz="0 0 ${floor_clearance+base_height/2}" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="${base_height}" radius="${base_width/2}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual>
|
||||
<origin xyz="0 0 ${floor_clearance+base_height/2}" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="${base_height}" radius="${base_width/2}"/>
|
||||
</geometry>
|
||||
<material name="orange"/>
|
||||
</visual>
|
||||
<visual>
|
||||
<origin xyz="${base_width/2 - 0.02} 0 ${floor_clearance+base_height+0.005}" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.01" radius="0.015"/>
|
||||
</geometry>
|
||||
<material name="red"/>
|
||||
</visual>
|
||||
|
||||
</link>
|
||||
|
||||
<joint name="${prefix}left_wheel_joint" type="continuous">
|
||||
<parent link="${prefix}base_link"/>
|
||||
<child link="${prefix}left_wheel"/>
|
||||
<origin xyz="0 ${wheel_distance/2} ${wheel_radius}" rpy="0 0 0"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0.2"/>
|
||||
<limit effort="100" velocity="1.0"/>
|
||||
</joint>
|
||||
|
||||
<!-- left wheel Link -->
|
||||
<link name="${prefix}left_wheel">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="${wheel_len}" radius="${wheel_radius}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="${wheel_len}" radius="${wheel_radius}"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
|
||||
</link>
|
||||
|
||||
<joint name="${prefix}right_wheel_joint" type="continuous">
|
||||
<parent link="${prefix}base_link"/>
|
||||
<child link="${prefix}right_wheel"/>
|
||||
<origin xyz="0 -${wheel_distance/2} ${wheel_radius}" rpy="0 0 0"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0.2"/>
|
||||
<limit effort="100" velocity="1.0"/>
|
||||
</joint>
|
||||
|
||||
<!-- right wheel Link -->
|
||||
<link name="${prefix}right_wheel">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="${wheel_len}" radius="${wheel_radius}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="${wheel_len}" radius="${wheel_radius}"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
|
||||
</link>
|
||||
|
||||
<joint name="${prefix}caster_frontal_wheel_joint" type="fixed">
|
||||
<parent link="${prefix}base_link"/>
|
||||
<child link="${prefix}caster_frontal_wheel"/>
|
||||
<origin xyz="${base_width/2 - caster_wheel_radius} 0 ${caster_wheel_radius}" rpy="0 0 0"/>
|
||||
</joint>
|
||||
|
||||
<!-- caster frontal wheel Link -->
|
||||
<link name="${prefix}caster_frontal_wheel">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${caster_wheel_radius}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${caster_wheel_radius}"/>
|
||||
</geometry>
|
||||
<material name="grey"/>
|
||||
</visual>
|
||||
|
||||
</link>
|
||||
|
||||
<joint name="${prefix}laser_joint" type="fixed">
|
||||
<parent link="${prefix}base_link"/>
|
||||
<child link="${prefix}laser"/>
|
||||
<origin xyz="0.025 0.025 ${floor_clearance+base_height+0.01}" rpy="0 0 0.66939"/>
|
||||
</joint>
|
||||
|
||||
<!-- laser Link -->
|
||||
<link name="${prefix}laser">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.03" radius="0.035"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.02" radius="0.035"/>
|
||||
</geometry>
|
||||
<material name="grey"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
104
hardware/include/cyphal_hardware_interface/simplesync_system.hpp
Normal file
104
hardware/include/cyphal_hardware_interface/simplesync_system.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2021 ros2_control Development Team
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef CYPHAL_HARDWARE_INTERFACE__SIMPLESYNC_SYSTEM_HPP_
|
||||
#define CYPHAL_HARDWARE_INTERFACE__SIMPLESYNC_SYSTEM_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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/clock.hpp"
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
|
||||
#include "rclcpp_lifecycle/state.hpp"
|
||||
|
||||
#include "cyphal_hardware_interface/visibility_control.h"
|
||||
|
||||
#include "simplesync.hpp"
|
||||
|
||||
namespace cyphal_hardware_interface {
|
||||
class cyphalSystemHardware : public hardware_interface::SystemInterface {
|
||||
public:
|
||||
RCLCPP_SHARED_PTR_DEFINITIONS(cyphalSystemHardware);
|
||||
|
||||
constexpr static std::string_view default_serial_port_ = "/dev/ttyACM0";
|
||||
|
||||
std::string serial_port_;
|
||||
bool try_next_port_number;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
virtual ~cyphalSystemHardware();
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
hardware_interface::CallbackReturn
|
||||
on_init(const hardware_interface::HardwareInfo &info) override;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
std::vector<hardware_interface::StateInterface>
|
||||
export_state_interfaces() override;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
std::vector<hardware_interface::CommandInterface>
|
||||
export_command_interfaces() override;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
hardware_interface::CallbackReturn
|
||||
on_configure(const rclcpp_lifecycle::State &previous_state) override;
|
||||
|
||||
hardware_interface::CallbackReturn
|
||||
on_activate(const rclcpp_lifecycle::State &previous_state) override;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
hardware_interface::CallbackReturn
|
||||
on_deactivate(const rclcpp_lifecycle::State &previous_state) override;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
hardware_interface::return_type read(const rclcpp::Time &time,
|
||||
const rclcpp::Duration &period) override;
|
||||
|
||||
CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
hardware_interface::return_type
|
||||
write(const rclcpp::Time &time, const rclcpp::Duration &period) override;
|
||||
|
||||
private:
|
||||
struct interface_params {
|
||||
double value;
|
||||
double factor;
|
||||
std::string joint;
|
||||
bool command;
|
||||
std::string control_interface;
|
||||
};
|
||||
|
||||
int serial_port_handle;
|
||||
|
||||
std::map<std::string, struct interface_params> interfaces_map;
|
||||
typedef simplesync::cyphal<
|
||||
std::chrono::time_point<std::chrono::steady_clock>,
|
||||
std::chrono::nanoseconds, 1024, true>
|
||||
cyphalChrono;
|
||||
std::unique_ptr<cyphalChrono> sync;
|
||||
};
|
||||
|
||||
} // namespace cyphal_hardware_interface
|
||||
|
||||
#endif // CYPHAL_HARDWARE_INTERFACE__SIMPLESYNC_SYSTEM_HPP_
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2021 ros2_control Development Team
|
||||
//
|
||||
// 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.
|
||||
|
||||
/* This header must be included by all rclcpp headers which declare symbols
|
||||
* which are defined in the rclcpp library. When not building the rclcpp
|
||||
* library, i.e. when using the headers in other package's code, the contents
|
||||
* of this header change the visibility of certain symbols which the rclcpp
|
||||
* library cannot have, but the consuming code must have inorder to link.
|
||||
*/
|
||||
|
||||
#ifndef CYPHAL_HARDWARE_INTERFACE__VISIBILITY_CONTROL_H_
|
||||
#define CYPHAL_HARDWARE_INTERFACE__VISIBILITY_CONTROL_H_
|
||||
|
||||
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
|
||||
// https://gcc.gnu.org/wiki/Visibility
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#ifdef __GNUC__
|
||||
#define CYPHAL_HARDWARE_INTERFACE_EXPORT __attribute__((dllexport))
|
||||
#define CYPHAL_HARDWARE_INTERFACE_IMPORT __attribute__((dllimport))
|
||||
#else
|
||||
#define CYPHAL_HARDWARE_INTERFACE_EXPORT __declspec(dllexport)
|
||||
#define CYPHAL_HARDWARE_INTERFACE_IMPORT __declspec(dllimport)
|
||||
#endif
|
||||
#ifdef CYPHAL_HARDWARE_INTERFACE_BUILDING_DLL
|
||||
#define CYPHAL_HARDWARE_INTERFACE_PUBLIC CYPHAL_HARDWARE_INTERFACE_EXPORT
|
||||
#else
|
||||
#define CYPHAL_HARDWARE_INTERFACE_PUBLIC CYPHAL_HARDWARE_INTERFACE_IMPORT
|
||||
#endif
|
||||
#define CYPHAL_HARDWARE_INTERFACE_PUBLIC_TYPE CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
#define CYPHAL_HARDWARE_INTERFACE_LOCAL
|
||||
#else
|
||||
#define CYPHAL_HARDWARE_INTERFACE_EXPORT __attribute__((visibility("default")))
|
||||
#define CYPHAL_HARDWARE_INTERFACE_IMPORT
|
||||
#if __GNUC__ >= 4
|
||||
#define CYPHAL_HARDWARE_INTERFACE_PUBLIC __attribute__((visibility("default")))
|
||||
#define CYPHAL_HARDWARE_INTERFACE_LOCAL __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define CYPHAL_HARDWARE_INTERFACE_PUBLIC
|
||||
#define CYPHAL_HARDWARE_INTERFACE_LOCAL
|
||||
#endif
|
||||
#define CYPHAL_HARDWARE_INTERFACE_PUBLIC_TYPE
|
||||
#endif
|
||||
|
||||
#endif // CYPHAL_HARDWARE_INTERFACE__VISIBILITY_CONTROL_H_
|
430
hardware/simplesync_system.cpp
Normal file
430
hardware/simplesync_system.cpp
Normal file
@ -0,0 +1,430 @@
|
||||
#include "cyphal_hardware_interface/simplesync_system.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <hardware_interface/hardware_info.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "serial.hpp"
|
||||
|
||||
#include "hardware_interface/types/hardware_interface_type_values.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "simplesync.hpp"
|
||||
|
||||
namespace cyphal_hardware_interface {
|
||||
|
||||
cyphalSystemHardware::~cyphalSystemHardware() {
|
||||
if (serial_port_handle) {
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Closing port '%s'", serial_port_.c_str());
|
||||
close(serial_port_handle);
|
||||
}
|
||||
}
|
||||
|
||||
hardware_interface::CallbackReturn cyphalSystemHardware::on_init(
|
||||
const hardware_interface::HardwareInfo &info) {
|
||||
if (hardware_interface::SystemInterface::on_init(info) !=
|
||||
hardware_interface::CallbackReturn::SUCCESS) {
|
||||
return hardware_interface::CallbackReturn::ERROR;
|
||||
}
|
||||
|
||||
serial_port_ = default_serial_port_;
|
||||
|
||||
if (info_.hardware_parameters.count("port") > 0) {
|
||||
serial_port_ = info_.hardware_parameters.at("port");
|
||||
} else {
|
||||
RCLCPP_WARN(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"No port specified in urdf, using default value");
|
||||
}
|
||||
if (info_.hardware_parameters.count("try_next_port_number") > 0) {
|
||||
std::string try_next_str =
|
||||
info_.hardware_parameters.at("try_next_port_number");
|
||||
try_next_port_number =
|
||||
std::set<std::string>({"True", "true", "1", "Yes", "yes", "Y", "y"})
|
||||
.count(try_next_str) > 0;
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Trying next ports if the one provided is not openable");
|
||||
}
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"), "Port set as %s",
|
||||
serial_port_.c_str());
|
||||
|
||||
std::vector<hardware_interface::ComponentInfo> components;
|
||||
components.insert(components.begin(), info_.joints.begin(),
|
||||
info_.joints.end());
|
||||
components.insert(components.begin(), info_.sensors.begin(),
|
||||
info_.sensors.end());
|
||||
components.insert(components.begin(), info_.gpios.begin(), info_.gpios.end());
|
||||
for (const hardware_interface::ComponentInfo &joint : components) {
|
||||
// StaubiSystem has exactly two states and one command interface on each
|
||||
// joint
|
||||
/*if (joint.command_interfaces.size() != 1) {
|
||||
RCLCPP_FATAL(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Joint '%s' has %zu command interfaces found. 1 expected.",
|
||||
joint.name.c_str(), joint.command_interfaces.size());
|
||||
return hardware_interface::CallbackReturn::ERROR;
|
||||
}*/
|
||||
for (const auto &i : joint.command_interfaces) {
|
||||
std::string param_name = "command:" + i.name;
|
||||
if (joint.parameters.count(param_name) <= 0) {
|
||||
RCLCPP_FATAL(
|
||||
rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"command interface '%s' of joint/gpio '%s' has no parameter "
|
||||
"'command:%s'.",
|
||||
i.name.c_str(), joint.name.c_str(), i.name.c_str());
|
||||
return hardware_interface::CallbackReturn::ERROR;
|
||||
}
|
||||
auto interface_name = joint.parameters.at(param_name);
|
||||
if (interfaces_map.count(interface_name) > 0) {
|
||||
RCLCPP_FATAL(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Sync Interface '%s' used twice. FATAL ERROR.",
|
||||
interface_name.c_str());
|
||||
return hardware_interface::CallbackReturn::ERROR;
|
||||
}
|
||||
interfaces_map[interface_name] = {
|
||||
std::numeric_limits<double>::quiet_NaN(),
|
||||
(joint.parameters.count("factor") > 0)
|
||||
? std::stod(joint.parameters.at("factor"))
|
||||
: 1.0,
|
||||
joint.name, true, i.name};
|
||||
}
|
||||
|
||||
for (const auto &i : joint.state_interfaces) {
|
||||
std::string param_name = "state:" + i.name;
|
||||
if (joint.parameters.count(param_name) <= 0) {
|
||||
RCLCPP_FATAL(
|
||||
rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"state interface '%s' of joint/sensor/gpio '%s' has no parameter "
|
||||
"'state:%s'.",
|
||||
i.name.c_str(), joint.name.c_str(), i.name.c_str());
|
||||
return hardware_interface::CallbackReturn::ERROR;
|
||||
}
|
||||
auto interface_name = joint.parameters.at(param_name);
|
||||
if (interfaces_map.count(interface_name) > 0) {
|
||||
RCLCPP_FATAL(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Sync Interface '%s' used twice. FATAL ERROR.",
|
||||
interface_name.c_str());
|
||||
return hardware_interface::CallbackReturn::ERROR;
|
||||
}
|
||||
interfaces_map[interface_name] = {
|
||||
std::numeric_limits<double>::quiet_NaN(),
|
||||
(joint.parameters.count("factor") > 0)
|
||||
? std::stod(joint.parameters.at("factor"))
|
||||
: 1.0,
|
||||
joint.name, false, i.name};
|
||||
}
|
||||
}
|
||||
// for (const auto &[key, value] : interfaces_map) {
|
||||
// RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
// "Interface %s added (factor: %f).", key.c_str(), value.factor);
|
||||
// }
|
||||
|
||||
return hardware_interface::CallbackReturn::SUCCESS;
|
||||
} // namespace cyphal_hardware_interface
|
||||
|
||||
std::vector<hardware_interface::StateInterface>
|
||||
cyphalSystemHardware::export_state_interfaces() {
|
||||
std::vector<hardware_interface::StateInterface> state_interfaces;
|
||||
for (auto &[key, value] : interfaces_map) {
|
||||
if (!value.command)
|
||||
state_interfaces.emplace_back(hardware_interface::StateInterface(
|
||||
value.joint, value.control_interface, &value.value));
|
||||
}
|
||||
/* for (auto i = 0u; i < info_.joints.size(); i++) {
|
||||
state_interfaces.emplace_back(hardware_interface::StateInterface(
|
||||
info_.joints[i].name, hardware_interface::HW_IF_POSITION,
|
||||
&hw_positions_[i]));
|
||||
state_interfaces.emplace_back(hardware_interface::StateInterface(
|
||||
info_.joints[i].name, hardware_interface::HW_IF_VELOCITY,
|
||||
&hw_velocities_[i]));
|
||||
}
|
||||
*/
|
||||
return state_interfaces;
|
||||
}
|
||||
|
||||
std::vector<hardware_interface::CommandInterface>
|
||||
cyphalSystemHardware::export_command_interfaces() {
|
||||
std::vector<hardware_interface::CommandInterface> command_interfaces;
|
||||
for (auto &[key, value] : interfaces_map) {
|
||||
if (value.command)
|
||||
command_interfaces.emplace_back(hardware_interface::CommandInterface(
|
||||
value.joint, value.control_interface, &value.value));
|
||||
}
|
||||
/* for (auto i = 0u; i < info_.joints.size(); i++) {
|
||||
command_interfaces.emplace_back(hardware_interface::CommandInterface(
|
||||
info_.joints[i].name, hardware_interface::HW_IF_VELOCITY,
|
||||
&hw_commands_[i]));
|
||||
}
|
||||
*/
|
||||
return command_interfaces;
|
||||
}
|
||||
|
||||
hardware_interface::CallbackReturn cyphalSystemHardware::on_configure(
|
||||
const rclcpp_lifecycle::State & /*previous_state*/) {
|
||||
// const hardware_interface::CallbackReturn result =
|
||||
// can_interface_->connect(can_socket_)
|
||||
// ? hardware_interface::CallbackReturn::SUCCESS
|
||||
// : hardware_interface::CallbackReturn::FAILURE;
|
||||
// Open the serial port. Change device path as needed (currently set to an
|
||||
// standard FTDI USB-UART cable type device)
|
||||
|
||||
serial_port_handle = open_serial(serial_port_);
|
||||
if (!serial_port_handle) {
|
||||
RCLCPP_FATAL(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Error opening port '%s': %s", serial_port_.c_str(),
|
||||
strerror(errno));
|
||||
if (try_next_port_number) {
|
||||
for (unsigned int tries = 0; tries < 100; tries += 1) {
|
||||
size_t digits_idx = serial_port_.size() - 1;
|
||||
while (digits_idx > 0 && std::isdigit(serial_port_[digits_idx]))
|
||||
digits_idx -= 1;
|
||||
digits_idx += 1;
|
||||
std::string number_str = serial_port_.substr(digits_idx);
|
||||
unsigned int number = number_str.size() > 0 ? std::stol(number_str) : 0;
|
||||
serial_port_ =
|
||||
serial_port_.substr(0, digits_idx) + std::to_string(number + 1);
|
||||
serial_port_handle = open_serial(serial_port_);
|
||||
if (!serial_port_handle) {
|
||||
RCLCPP_FATAL(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Error opening port '%s': %s", serial_port_.c_str(),
|
||||
strerror(errno));
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!serial_port_handle)
|
||||
return hardware_interface::CallbackReturn::FAILURE;
|
||||
}
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Successfuly opened port '%s'", serial_port_.c_str());
|
||||
|
||||
sync = std::make_unique<cyphalChrono>(
|
||||
[this](uint8_t *buf, unsigned int buf_size) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex;
|
||||
for (unsigned int i = 0; i < buf_size; i += 1)
|
||||
ss << (int)buf[i] << " ";
|
||||
|
||||
// RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
// "Write to serial: %s", ss.str().c_str());
|
||||
write_serial(this->serial_port_handle, buf, buf_size);
|
||||
return 0;
|
||||
},
|
||||
[]() { return (std::chrono::steady_clock::now()); });
|
||||
|
||||
sync->request_all_interfaces();
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(100ms); // FIXME: callback
|
||||
uint8_t read_buf[1024];
|
||||
int read_buf_used =
|
||||
read_serial(serial_port_handle, read_buf, sizeof(read_buf));
|
||||
|
||||
sync->handle_stream((uint8_t *)read_buf, read_buf_used);
|
||||
|
||||
for (auto &i : sync->interfaces) {
|
||||
if (!i)
|
||||
continue;
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Sync-interface %s recived.", i->key.c_str());
|
||||
}
|
||||
|
||||
return hardware_interface::CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
hardware_interface::CallbackReturn cyphalSystemHardware::on_activate(
|
||||
const rclcpp_lifecycle::State & /*previous_state*/) {
|
||||
// BEGIN: This part here is for exemplary purposes - Please do not copy to
|
||||
// your production code
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Activating ...please wait...");
|
||||
|
||||
/* for (auto i = 0; i < hw_start_sec_; i++) {
|
||||
rclcpp::sleep_for(std::chrono::seconds(1));
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"%.1f seconds left...", hw_start_sec_ - i);
|
||||
}
|
||||
// END: This part here is for exemplary purposes - Please do not copy to
|
||||
your
|
||||
// production code
|
||||
|
||||
// set some default values
|
||||
for (auto i = 0u; i < hw_positions_.size(); i++) {
|
||||
if (std::isnan(hw_positions_[i])) {
|
||||
hw_positions_[i] = 0;
|
||||
hw_velocities_[i] = 0;
|
||||
hw_commands_[i] = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Successfully activated!");
|
||||
|
||||
return hardware_interface::CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
hardware_interface::CallbackReturn cyphalSystemHardware::on_deactivate(
|
||||
const rclcpp_lifecycle::State & /*previous_state*/) {
|
||||
// BEGIN: This part here is for exemplary purposes - Please do not copy to
|
||||
// your production code
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Deactivating ...please wait...");
|
||||
|
||||
/* for (auto i = 0; i < hw_stop_sec_; i++) {
|
||||
rclcpp::sleep_for(std::chrono::seconds(1));
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"%.1f seconds left...", hw_stop_sec_ - i);
|
||||
}
|
||||
// END: This part here is for exemplary purposes - Please do not copy to
|
||||
your
|
||||
// production code
|
||||
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Successfully deactivated!");
|
||||
*/
|
||||
return hardware_interface::CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
hardware_interface::return_type
|
||||
cyphalSystemHardware::read(const rclcpp::Time & /*time*/,
|
||||
const rclcpp::Duration & /*period*/) {
|
||||
// BEGIN: This part here is for exemplary purposes - Please do not copy to
|
||||
// your production code
|
||||
/* for (std::size_t i = 0; i < hw_velocities_.size(); i++) {
|
||||
// Simulate Staubi wheels's movement as a first-order system
|
||||
// Update the joint status: this is a revolute joint without any limit.
|
||||
// Simply integrates
|
||||
hw_positions_[i] = hw_positions_[i] + period.seconds() *
|
||||
hw_velocities_[i];
|
||||
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Got position state %.5f and velocity state %.5f for '%s'!",
|
||||
hw_positions_[i], hw_velocities_[i],
|
||||
info_.joints[i].name.c_str());
|
||||
}
|
||||
// END: This part here is for exemplary purposes - Please do not copy to
|
||||
your
|
||||
// production code
|
||||
*/
|
||||
|
||||
// Allocate memory for read buffer, set size according to your needs
|
||||
|
||||
// Read bytes. The behaviour of read() (e.g. does it block?,
|
||||
// how long does it block for?) depends on the configuration
|
||||
// settings above, specifically VMIN and VTIME
|
||||
// char read_buf[256];
|
||||
// int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));
|
||||
|
||||
// // n is the number of bytes read. n may be 0 if no bytes were received,
|
||||
// and
|
||||
// // can also be -1 to signal an error.
|
||||
// if (num_bytes < 0) {
|
||||
// printf("Error reading: %s", strerror(errno));
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// // Here we assume we received ASCII data, but you might be sending raw
|
||||
// bytes
|
||||
// // (in that case, don't try and print it to the screen like this!)
|
||||
// printf("Read %i bytes. Received message: %s", num_bytes, read_buf);
|
||||
|
||||
// close(serial_port);
|
||||
if (!sync)
|
||||
return hardware_interface::return_type::ERROR;
|
||||
|
||||
uint8_t read_buf[1024];
|
||||
int read_buf_used =
|
||||
read_serial(serial_port_handle, read_buf, sizeof(read_buf));
|
||||
sync->handle_stream((uint8_t *)read_buf, read_buf_used);
|
||||
for (auto &[key, value] : interfaces_map) {
|
||||
// RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
// "Interface %s added (factor: %f).", key.c_str(),
|
||||
// value.factor);
|
||||
if (!value.command) {
|
||||
auto n = sync->get_number(key.c_str());
|
||||
if (n)
|
||||
value.value = value.factor * (*n);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
if (std::chrono::steady_clock::now() - sync->last_pkg_recived >
|
||||
std::chrono::milliseconds(1000)) {
|
||||
RCLCPP_FATAL(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"last package recivew more than 1000ms ago. aborting!");
|
||||
return hardware_interface::return_type::ERROR;
|
||||
}
|
||||
|
||||
static int counter = 0;
|
||||
if (counter++ % 20 == 0)
|
||||
for (auto &i : sync->interfaces) {
|
||||
if (!i)
|
||||
continue;
|
||||
auto number_ptr = sync->get_number(i->key.c_str());
|
||||
auto str_ptr = sync->get_string(i->key.c_str());
|
||||
if (number_ptr)
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Sync: %s = %d", i->key.c_str(), *number_ptr);
|
||||
if (str_ptr)
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Sync: %s = %s", i->key.c_str(), str_ptr->c_str());
|
||||
}
|
||||
|
||||
return hardware_interface::return_type::OK;
|
||||
}
|
||||
|
||||
hardware_interface::return_type
|
||||
cyphal_hardware_interface ::cyphalSystemHardware::write(
|
||||
const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/) {
|
||||
// BEGIN: This part here is for exemplary purposes - Please do not copy to
|
||||
// your production code
|
||||
|
||||
if (sync) {
|
||||
for (auto &[key, value] : interfaces_map) {
|
||||
// RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
// "Interface %s added (factor: %f).", key.c_str(),
|
||||
// value.factor);
|
||||
if (value.command) {
|
||||
auto ni = sync->get_or_create_interface(0x01, key.c_str());
|
||||
if (ni) {
|
||||
|
||||
*(int *)ni->data = (value.value / value.factor);
|
||||
ni->send_requested = true;
|
||||
|
||||
// RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
// "update: %s set to %d ", key.c_str(), *(int
|
||||
// *)ni->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
sync->update();
|
||||
}
|
||||
|
||||
/* for (auto i = 0u; i < hw_commands_.size(); i++) {
|
||||
// Simulate sending commands to the hardware
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Got command %.5f for '%s'!", hw_commands_[i],
|
||||
info_.joints[i].name.c_str());
|
||||
|
||||
hw_velocities_[i] = hw_commands_[i];
|
||||
}
|
||||
RCLCPP_INFO(rclcpp::get_logger("cyphalSystemHardware"),
|
||||
"Joints successfully written!");
|
||||
// END: This part here is for exemplary purposes - Please do not copy to
|
||||
your
|
||||
// production code
|
||||
*/
|
||||
return hardware_interface::return_type::OK;
|
||||
}
|
||||
|
||||
} // namespace cyphal_hardware_interface
|
||||
|
||||
#include "pluginlib/class_list_macros.hpp"
|
||||
PLUGINLIB_EXPORT_CLASS(cyphal_hardware_interface::cyphalSystemHardware,
|
||||
hardware_interface::SystemInterface)
|
40
package.xml
Normal file
40
package.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?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>cyphal_hardware_interface</name>
|
||||
<version>0.0.1</version>
|
||||
<description>`ros2_control` hardware interface for devices talking cyphal (udral_servo)</description>
|
||||
|
||||
<maintainer email="git@nilsschulte.de">Nils Schulte</maintainer>
|
||||
|
||||
<author email="git@nilsschulte.de">Nils Schulte</author>
|
||||
|
||||
<license>EUPLv1.2</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>hardware_interface</depend>
|
||||
<depend>pluginlib</depend>
|
||||
<depend>rclcpp</depend>
|
||||
<depend>rclcpp_lifecycle</depend>
|
||||
|
||||
<exec_depend>controller_manager</exec_depend>
|
||||
<exec_depend>diff_drive_controller</exec_depend>
|
||||
<exec_depend>joint_state_broadcaster</exec_depend>
|
||||
<exec_depend>robot_state_publisher</exec_depend>
|
||||
<exec_depend>ros2controlcli</exec_depend>
|
||||
<exec_depend>ros2launch</exec_depend>
|
||||
<exec_depend>rviz2</exec_depend>
|
||||
<exec_depend>xacro</exec_depend>
|
||||
|
||||
<test_depend>ament_cmake_gtest</test_depend>
|
||||
<test_depend>ament_cmake_pytest</test_depend>
|
||||
<test_depend>launch_testing_ament_cmake</test_depend>
|
||||
<test_depend>launch_testing_ros</test_depend>
|
||||
<test_depend>liburdfdom-tools</test_depend>
|
||||
<test_depend>xacro</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
Loading…
x
Reference in New Issue
Block a user