add rm_arm_control

This commit is contained in:
hivecore
2025-09-05 19:05:48 +08:00
parent f180b8a57e
commit 4a31c1f3cb
53 changed files with 22526 additions and 1920 deletions

View File

@@ -1,156 +0,0 @@
import os
from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
# 获取包的共享目录
driver_pkg_dir = get_package_share_directory('driver_pkg')
perception_pkg_dir = get_package_share_directory('perception_pkg')
decision_pkg_dir = get_package_share_directory('decision_pkg')
control_pkg_dir = get_package_share_directory('control_pkg')
interaction_pkg_dir = get_package_share_directory('interaction_pkg')
# 配置文件路径
nav2_config = os.path.join(control_pkg_dir, 'config', 'nav2_config.yaml')
return LaunchDescription([
# ========== 驱动层节点 ==========
# 电机驱动
Node(
package='driver_pkg',
executable='motor_driver_node',
name='motor_driver',
output='screen'
),
# 激光雷达驱动
Node(
package='driver_pkg',
executable='lidar_driver_node',
name='lidar_driver',
output='screen'
),
# 相机驱动
Node(
package='driver_pkg',
executable='camera_driver_node',
name='camera_driver',
output='screen'
),
# ========== 感知层节点 ==========
# 点云处理
Node(
package='perception_pkg',
executable='point_cloud_processor',
name='point_cloud_processor',
output='screen'
),
# 目标检测
Node(
package='perception_pkg',
executable='object_detection_node.py',
name='object_detection',
output='screen'
),
# SLAM (使用Cartographer)
Node(
package='cartographer_ros',
executable='cartographer_node',
name='cartographer_node',
output='screen',
arguments=[
'-configuration_directory', os.path.join(perception_pkg_dir, 'config'),
'-configuration_basename', 'cartographer_config.lua'
]
),
Node(
package='cartographer_ros',
executable='occupancy_grid_node',
name='occupancy_grid_node',
output='screen',
arguments=['-resolution', '0.05']
),
# ========== 决策层节点 ==========
# 行为树
Node(
package='decision_pkg',
executable='behavior_tree_node.py',
name='behavior_tree',
output='screen'
),
# 任务规划
Node(
package='decision_pkg',
executable='task_planner_node.py',
name='task_planner',
output='screen'
),
# 路径规划 (使用Nav2)
Node(
package='nav2_bringup',
executable='nav2_lifecycle_manager',
name='lifecycle_manager_navigation',
output='screen',
parameters=[{
'autostart': True,
'node_names': [
'controller_server',
'planner_server',
'recoveries_server',
'bt_navigator',
'waypoint_follower'
]
}]
),
Node(
package='nav2_planner',
executable='planner_server',
name='planner_server',
output='screen',
parameters=[nav2_config]
),
Node(
package='nav2_controller',
executable='controller_server',
name='controller_server',
output='screen',
parameters=[nav2_config]
),
# ========== 控制层节点 ==========
# 底盘控制器
Node(
package='control_pkg',
executable='base_controller',
name='base_controller',
output='screen'
),
# ========== 交互层节点 ==========
# 状态监控
Node(
package='interaction_pkg',
executable='robot_monitor.py',
name='robot_monitor',
output='screen'
),
# Web桥接器
Node(
package='rosbridge_server',
executable='rosbridge_websocket',
name='rosbridge_websocket',
output='screen'
)
])

View File

@@ -1,62 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(control_pkg)
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(rclpy REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(trajectory_msgs REQUIRED)
find_package(control_msgs REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_ros REQUIRED)
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()
# 添加可执行文件
add_executable(arm_controller_node src/arm_controller.cpp)
ament_target_dependencies(arm_controller_node
rclcpp
sensor_msgs
trajectory_msgs
control_msgs
rclcpp_action
nav_msgs
tf2 # 添加TF2依赖
tf2_ros # 添加TF2 ROS依赖
)
# 添加可执行文件
add_executable(base_controller_node src/base_controller.cpp)
ament_target_dependencies(base_controller_node
rclcpp
sensor_msgs
nav_msgs
tf2 # 添加TF2依赖
tf2_ros # 添加TF2 ROS依赖
)
# 安装可执行文件
install(TARGETS
arm_controller_node
base_controller_node
DESTINATION lib/${PROJECT_NAME})
ament_package()

View File

@@ -1,24 +0,0 @@
<?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>control_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ray@todo.todo">ray</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclpy</depend>
<depend>control_msgs</depend>
<depend>rclcpp_action</depend>
<depend>nav_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,200 +0,0 @@
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/joint_state.hpp"
#include "trajectory_msgs/msg/joint_trajectory.hpp"
#include "control_msgs/action/follow_joint_trajectory.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include <vector>
#include <string>
#include <memory>
#include <chrono>
using namespace std::chrono_literals;
using FollowJointTrajectory = control_msgs::action::FollowJointTrajectory;
using GoalHandleFollowJointTrajectory = rclcpp_action::ClientGoalHandle<FollowJointTrajectory>;
class ArmController : public rclcpp::Node
{
public:
ArmController() : Node("arm_controller")
{
// 声明参数
this->declare_parameter<std::vector<std::string>>("joint_names",
std::vector<std::string>{"shoulder_pan_joint", "shoulder_lift_joint",
"elbow_joint", "wrist_1_joint",
"wrist_2_joint", "wrist_3_joint"});
// 获取关节名称
joint_names_ = this->get_parameter("joint_names").as_string_array();
// 创建关节状态发布者
joint_state_pub_ = this->create_publisher<sensor_msgs::msg::JointState>(
"joint_states", 10);
// 创建轨迹动作客户端
trajectory_client_ = rclcpp_action::create_client<FollowJointTrajectory>(
this, "follow_joint_trajectory");
// 创建定时器,定期发布关节状态
timer_ = this->create_wall_timer(
10ms, std::bind(&ArmController::publishJointStates, this));
// 初始化关节状态
initJointStates();
RCLCPP_INFO(this->get_logger(), "Arm controller initialized");
}
// 执行关节轨迹
void executeTrajectory(const std::vector<std::vector<double>>& waypoints,
const std::vector<double>& time_points)
{
// 等待动作服务器可用
if (!trajectory_client_->wait_for_action_server(10s)) {
RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting");
return;
}
// 创建目标
auto goal_msg = FollowJointTrajectory::Goal();
goal_msg.trajectory.joint_names = joint_names_;
// 添加轨迹点
for (size_t i = 0; i < waypoints.size(); ++i) {
trajectory_msgs::msg::JointTrajectoryPoint point;
point.positions = waypoints[i];
// 设置时间戳
rclcpp::Duration time_from_start = rclcpp::Duration::from_seconds(time_points[i]);
builtin_interfaces::msg::Duration duration_msg;
rclcpp::Duration rclcpp_duration = rclcpp::Duration::from_seconds(time_points[i]);
// 设置秒和纳秒部分
duration_msg.sec = rclcpp_duration.seconds();
duration_msg.nanosec = rclcpp_duration.nanoseconds() % 1000000000;
point.time_from_start = duration_msg;
goal_msg.trajectory.points.push_back(point);
}
// 发送目标
auto send_goal_options = rclcpp_action::Client<FollowJointTrajectory>::SendGoalOptions();
send_goal_options.feedback_callback =
[this](GoalHandleFollowJointTrajectory::SharedPtr,
const std::shared_ptr<const FollowJointTrajectory::Feedback> feedback) {
RCLCPP_INFO(this->get_logger(), "Current position: %.2f, %.2f, %.2f",
feedback->actual.positions[0],
feedback->actual.positions[1],
feedback->actual.positions[2]);
};
send_goal_options.result_callback =
[this](const GoalHandleFollowJointTrajectory::WrappedResult& result) {
switch (result.code) {
case rclcpp_action::ResultCode::SUCCEEDED:
RCLCPP_INFO(this->get_logger(), "Trajectory execution succeeded");
break;
case rclcpp_action::ResultCode::ABORTED:
RCLCPP_ERROR(this->get_logger(), "Trajectory execution aborted");
return;
case rclcpp_action::ResultCode::CANCELED:
RCLCPP_INFO(this->get_logger(), "Trajectory execution canceled");
return;
default:
RCLCPP_ERROR(this->get_logger(), "Unknown result code");
return;
}
};
auto goal_handle_future = trajectory_client_->async_send_goal(goal_msg, send_goal_options);
if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), goal_handle_future) !=
rclcpp::FutureReturnCode::SUCCESS) {
RCLCPP_ERROR(this->get_logger(), "send goal call failed :(");
return;
}
goal_handle_ = goal_handle_future.get();
if (!goal_handle_) {
RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server");
return;
}
RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result");
}
// 简单的运动示例:将机械臂移动到预设位置
void moveToHomePosition()
{
// 定义路径点(关节角度)
std::vector<std::vector<double>> waypoints;
std::vector<double> time_points;
// 当前位置(假设)
waypoints.push_back(current_positions_);
time_points.push_back(0.0);
// 目标位置(示例)
std::vector<double> home_positions = {0.0, -1.57, 1.57, 0.0, 0.0, 0.0};
waypoints.push_back(home_positions);
time_points.push_back(3.0); // 3秒内到达
// 执行轨迹
executeTrajectory(waypoints, time_points);
}
private:
void initJointStates()
{
// 初始化关节状态
joint_state_msg_.header.frame_id = "base_link";
joint_state_msg_.name = joint_names_;
// 初始化关节位置全部为0
current_positions_.resize(joint_names_.size(), 0.0);
joint_state_msg_.position = current_positions_;
// 初始化速度和加速度
joint_state_msg_.velocity.resize(joint_names_.size(), 0.0);
joint_state_msg_.effort.resize(joint_names_.size(), 0.0);
}
void publishJointStates()
{
// 更新时间戳
joint_state_msg_.header.stamp = this->now();
// 发布关节状态
joint_state_pub_->publish(joint_state_msg_);
}
// 关节状态发布者
rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr joint_state_pub_;
// 轨迹动作客户端
rclcpp_action::Client<FollowJointTrajectory>::SharedPtr trajectory_client_;
GoalHandleFollowJointTrajectory::SharedPtr goal_handle_;
// 定时器
rclcpp::TimerBase::SharedPtr timer_;
// 关节状态消息
sensor_msgs::msg::JointState joint_state_msg_;
// 关节名称和当前位置
std::vector<std::string> joint_names_;
std::vector<double> current_positions_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<ArmController>();
// 示例:移动到预设位置
node->moveToHomePosition();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}

View File

@@ -1,118 +0,0 @@
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "nav_msgs/msg/odometry.hpp"
#include "tf2/LinearMath/Quaternion.h"
#include "tf2_ros/transform_broadcaster.h"
class BaseController : public rclcpp::Node
{
public:
BaseController() : Node("base_controller")
{
// 订阅参考速度
cmd_vel_sub_ = this->create_subscription<geometry_msgs::msg::Twist>(
"cmd_vel", 10, std::bind(&BaseController::cmdVelCallback, this, std::placeholders::_1));
// 发布里程计
odom_pub_ = this->create_publisher<nav_msgs::msg::Odometry>("odom", 10);
// 创建TF广播器
tf_broadcaster_ = std::make_unique<tf2_ros::TransformBroadcaster>(*this);
// 初始化状态
x_ = 0.0;
y_ = 0.0;
theta_ = 0.0;
// 创建定时器,定期更新控制和发布状态
timer_ = this->create_wall_timer(
std::chrono::milliseconds(20), std::bind(&BaseController::controlLoop, this));
}
private:
void cmdVelCallback(const geometry_msgs::msg::Twist::SharedPtr msg)
{
// 存储目标速度
target_vx_ = msg->linear.x;
target_vy_ = msg->linear.y;
target_w_ = msg->angular.z;
}
void controlLoop()
{
// 简单PID控制器实现
double dt = 0.02; // 20ms控制周期
// 模拟速度控制
current_vx_ = target_vx_; // 简化模型实际应包含PID控制
current_vy_ = target_vy_;
current_w_ = target_w_;
// 更新位姿
double delta_x = (current_vx_ * cos(theta_) - current_vy_ * sin(theta_)) * dt;
double delta_y = (current_vx_ * sin(theta_) + current_vy_ * cos(theta_)) * dt;
double delta_theta = current_w_ * dt;
x_ += delta_x;
y_ += delta_y;
theta_ += delta_theta;
// 发布TF变换
geometry_msgs::msg::TransformStamped transform_stamped;
transform_stamped.header.stamp = this->now();
transform_stamped.header.frame_id = "odom";
transform_stamped.child_frame_id = "base_link";
transform_stamped.transform.translation.x = x_;
transform_stamped.transform.translation.y = y_;
transform_stamped.transform.translation.z = 0.0;
tf2::Quaternion q;
q.setRPY(0, 0, theta_);
transform_stamped.transform.rotation.x = q.x();
transform_stamped.transform.rotation.y = q.y();
transform_stamped.transform.rotation.z = q.z();
transform_stamped.transform.rotation.w = q.w();
tf_broadcaster_->sendTransform(transform_stamped);
// 发布里程计消息
auto odom = std::make_unique<nav_msgs::msg::Odometry>();
odom->header.stamp = this->now();
odom->header.frame_id = "odom";
odom->child_frame_id = "base_link";
odom->pose.pose.position.x = x_;
odom->pose.pose.position.y = y_;
odom->pose.pose.position.z = 0.0;
odom->pose.pose.orientation.x = q.x();
odom->pose.pose.orientation.y = q.y();
odom->pose.pose.orientation.z = q.z();
odom->pose.pose.orientation.w = q.w();
odom->twist.twist.linear.x = current_vx_;
odom->twist.twist.linear.y = current_vy_;
odom->twist.twist.angular.z = current_w_;
odom_pub_->publish(std::move(odom));
}
rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr cmd_vel_sub_;
rclcpp::Publisher<nav_msgs::msg::Odometry>::SharedPtr odom_pub_;
std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
rclcpp::TimerBase::SharedPtr timer_;
// 机器人状态
double x_, y_, theta_;
double target_vx_, target_vy_, target_w_;
double current_vx_, current_vy_, current_w_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<BaseController>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}

View File

@@ -1,25 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(decision_pkg)
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(rclpy REQUIRED)
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()

View File

@@ -1,21 +0,0 @@
<?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>decision_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ray@todo.todo">ray</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclpy</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,108 +0,0 @@
import rclpy
from rclpy.node import Node
from py_trees.behaviour import Behaviour
from py_trees.common import Status
from py_trees.composites import Sequence, Selector
from py_trees.trees import BehaviourTree
from nav2_msgs.action import NavigateToPose
from rclpy.action import ActionClient
class MoveBaseAction(Behaviour):
def __init__(self, name, goal_pose):
super(MoveBaseAction, self).__init__(name)
self.goal_pose = goal_pose
self.action_client = None
self.goal_handle = None
self.feedback = None
self.result_future = None
def setup(self, **kwargs):
node = kwargs.get('node')
self.action_client = ActionClient(node, NavigateToPose, 'navigate_to_pose')
self.action_client.wait_for_server()
return True
def initialise(self):
goal_msg = NavigateToPose.Goal()
goal_msg.pose = self.goal_pose
self.goal_handle = self.action_client.send_goal_async(
goal_msg, feedback_callback=self.feedback_callback)
self.result_future = self.goal_handle.add_done_callback(self.goal_response_callback)
def update(self):
if self.result_future.done():
goal_status = self.goal_handle.result().status
if goal_status == 4: # SUCCEEDED
return Status.SUCCESS
else:
return Status.FAILURE
return Status.RUNNING
def terminate(self, new_status):
if self.action_client.is_server_ready() and self.goal_handle is not None:
self.action_client.cancel_goal_async(self.goal_handle)
def goal_response_callback(self, future):
goal_handle = future.result()
if not goal_handle.accepted:
self.logger.error('Goal rejected by server')
return
self.logger.info('Goal accepted by server, waiting for result')
self.result_future = goal_handle.get_result_async()
self.result_future.add_done_callback(self.get_result_callback)
def get_result_callback(self, future):
result = future.result().result
self.logger.info(f'Navigation result: {result}')
def feedback_callback(self, feedback_msg):
self.feedback = feedback_msg.feedback
# 处理反馈信息
class BehaviorTreeNode(Node):
def __init__(self):
super().__init__('behavior_tree_node')
# 创建行为树
self.create_behavior_tree()
# 创建定时器,定期执行行为树
self.timer = self.create_timer(0.1, self.execute_behavior_tree)
self.get_logger().info('Behavior tree node initialized')
def create_behavior_tree(self):
# 定义目标位置
goal_pose1 = ... # 填充目标位姿
goal_pose2 = ...
# 创建行为
move_to_kitchen = MoveBaseAction("Move to Kitchen", goal_pose1)
move_to_living_room = MoveBaseAction("Move to Living Room", goal_pose2)
# 创建序列
sequence = Sequence("Main Sequence")
sequence.add_children([move_to_kitchen, move_to_living_room])
# 创建行为树
self.tree = BehaviourTree(sequence)
# 设置节点
self.tree.setup(node=self)
def execute_behavior_tree(self):
# 执行行为树
self.tree.tick()
def main(args=None):
rclpy.init(args=args)
node = BehaviorTreeNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()

View File

@@ -1,65 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(driver_pkg)
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(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(OpenCV REQUIRED)
find_package(serial_driver REQUIRED)
find_package(cv_bridge REQUIRED)
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()
# 添加电机驱动可执行文件
add_executable(motor_driver_node src/motor_driver_node.cpp)
ament_target_dependencies(motor_driver_node
rclcpp
geometry_msgs
sensor_msgs
serial_driver
OpenCV # 添加OpenCV依赖
cv_bridge # 添加cv_bridge依赖
)
# 添加激光雷达驱动可执行文件
add_executable(lidar_driver_node src/lidar_driver_node.cpp)
ament_target_dependencies(lidar_driver_node
rclcpp
sensor_msgs
serial_driver
)
# 添加相机驱动可执行文件
add_executable(camera_driver_node src/camera_driver_node.cpp)
ament_target_dependencies(camera_driver_node
rclcpp
sensor_msgs
serial_driver
OpenCV # 添加OpenCV依赖
cv_bridge # 添加cv_bridge依赖
)
# 安装可执行文件
install(TARGETS
motor_driver_node
lidar_driver_node
camera_driver_node
DESTINATION lib/${PROJECT_NAME})
ament_package()

View File

@@ -1,27 +0,0 @@
<?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>driver_pkg</name>
<version>0.0.0</version>
<description>Robot driver package</description>
<maintainer email="ray@hivecore.cn">ray</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>geometry_msgs</depend>
<depend>sensor_msgs</depend>
<depend>serial_driver</depend>
<depend>OpenCV</depend>
<depend>cv_bridge</depend>
<depend>tf2</depend>
<depend>tf2_ros</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,145 +0,0 @@
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/image.hpp"
#include "cv_bridge/cv_bridge.h"
#include <opencv2/opencv.hpp>
#include "serial/serial.h"
#include <string>
#include <thread>
#include <chrono>
class CameraDriverNode : public rclcpp::Node
{
public:
CameraDriverNode() : Node("camera_driver_node")
{
// 声明并获取参数
this->declare_parameter<int>("camera_id", 0);
this->declare_parameter<std::string>("frame_id", "camera_frame");
this->declare_parameter<int>("width", 640);
this->declare_parameter<int>("height", 480);
this->declare_parameter<double>("fps", 30.0);
camera_id_ = this->get_parameter("camera_id").as_int();
frame_id_ = this->get_parameter("frame_id").as_string();
width_ = this->get_parameter("width").as_int();
height_ = this->get_parameter("height").as_int();
fps_ = this->get_parameter("fps").as_double();
// 创建图像消息发布者
image_pub_ = this->create_publisher<sensor_msgs::msg::Image>("image_raw", 10);
// 初始化相机
if (!initCamera()) {
RCLCPP_ERROR(this->get_logger(), "Failed to initialize camera");
return;
}
// 创建图像采集线程
image_thread_ = std::thread(&CameraDriverNode::imageCaptureLoop, this);
RCLCPP_INFO(this->get_logger(), "Camera driver node initialized");
}
~CameraDriverNode()
{
// 停止图像采集线程
running_ = false;
if (image_thread_.joinable()) {
image_thread_.join();
}
// 释放相机资源
if (cap_.isOpened()) {
cap_.release();
}
}
private:
bool initCamera()
{
// 打开相机
cap_.open(camera_id_);
if (!cap_.isOpened()) {
RCLCPP_ERROR(this->get_logger(), "Unable to open camera %d", camera_id_);
return false;
}
// 设置相机参数
cap_.set(cv::CAP_PROP_FRAME_WIDTH, width_);
cap_.set(cv::CAP_PROP_FRAME_HEIGHT, height_);
cap_.set(cv::CAP_PROP_FPS, fps_);
// 检查参数设置是否成功
int actual_width = static_cast<int>(cap_.get(cv::CAP_PROP_FRAME_WIDTH));
int actual_height = static_cast<int>(cap_.get(cv::CAP_PROP_FRAME_HEIGHT));
double actual_fps = cap_.get(cv::CAP_PROP_FPS);
RCLCPP_INFO(this->get_logger(), "Camera parameters: width=%d, height=%d, fps=%.1f",
actual_width, actual_height, actual_fps);
// 捕获一帧测试
cv::Mat test_frame;
if (!cap_.read(test_frame)) {
RCLCPP_ERROR(this->get_logger(), "Failed to capture test frame from camera");
return false;
}
return true;
}
void imageCaptureLoop()
{
running_ = true;
rclcpp::Rate rate(fps_);
while (running_ && rclcpp::ok()) {
// 捕获图像
cv::Mat frame;
if (cap_.read(frame)) {
// 转换为ROS图像消息
auto image_msg = cv_bridge::CvImage(
std_msgs::msg::Header(),
"bgr8",
frame
).toImageMsg();
// 设置时间戳和坐标系
image_msg->header.stamp = this->now();
image_msg->header.frame_id = frame_id_;
// 发布图像消息
image_pub_->publish(*image_msg);
} else {
RCLCPP_WARN(this->get_logger(), "Failed to capture frame from camera");
}
rate.sleep();
}
}
// 相机对象
cv::VideoCapture cap_;
// 参数
int camera_id_;
std::string frame_id_;
int width_;
int height_;
double fps_;
// 发布者
rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_pub_;
// 图像采集线程
std::thread image_thread_;
bool running_{false};
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<CameraDriverNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}

View File

@@ -1,214 +0,0 @@
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/laser_scan.hpp"
#include <string>
#include <vector>
#include <thread>
#include <chrono>
class LidarDriverNode : public rclcpp::Node
{
public:
LidarDriverNode() : Node("lidar_driver_node")
{
// 声明并获取参数
this->declare_parameter<std::string>("port", "/dev/ttyUSB0");
this->declare_parameter<int>("baudrate", 115200);
this->declare_parameter<std::string>("frame_id", "laser_frame");
this->declare_parameter<double>("frequency", 10.0);
port_ = this->get_parameter("port").as_string();
baudrate_ = this->get_parameter("baudrate").as_int();
frame_id_ = this->get_parameter("frame_id").as_string();
frequency_ = this->get_parameter("frequency").as_double();
// 创建激光扫描消息发布者
scan_pub_ = this->create_publisher<sensor_msgs::msg::LaserScan>("scan", 10);
// 初始化LIDAR
if (!initLidar()) {
RCLCPP_ERROR(this->get_logger(), "Failed to initialize LIDAR");
return;
}
// 创建数据采集线程
data_thread_ = std::thread(&LidarDriverNode::dataCollectionLoop, this);
RCLCPP_INFO(this->get_logger(), "LIDAR driver node initialized");
}
~LidarDriverNode()
{
// 停止数据采集线程
running_ = false;
if (data_thread_.joinable()) {
data_thread_.join();
}
// 关闭串口
if (serial_port_.isOpen()) {
serial_port_.close();
}
}
private:
bool initLidar()
{
try {
// 配置串口
serial_port_.setPort(port_);
serial_port_.setBaudrate(baudrate_);
serial::Timeout timeout = serial::Timeout::simpleTimeout(1000);
serial_port_.setTimeout(timeout);
// 打开串口
serial_port_.open();
if (serial_port_.isOpen()) {
RCLCPP_INFO(this->get_logger(), "Successfully connected to LIDAR on port %s", port_.c_str());
// 发送初始化命令根据具体LIDAR型号调整
std::string init_cmd = "start_scan\n";
serial_port_.write(init_cmd);
// 等待LIDAR初始化
std::this_thread::sleep_for(std::chrono::seconds(1));
return true;
} else {
RCLCPP_ERROR(this->get_logger(), "Unable to open port %s", port_.c_str());
return false;
}
} catch (serial::SerialException& e) {
RCLCPP_ERROR(this->get_logger(), "Serial exception: %s", e.what());
return false;
}
}
void dataCollectionLoop()
{
running_ = true;
rclcpp::Rate rate(frequency_);
// 激光扫描消息初始化
auto scan_msg = std::make_unique<sensor_msgs::msg::LaserScan>();
scan_msg->header.frame_id = frame_id_;
// 假设LIDAR参数根据实际LIDAR型号调整
scan_msg->angle_min = -3.141592; // -180度
scan_msg->angle_max = 3.141592; // 180度
scan_msg->angle_increment = 0.0174533; // 约1度
scan_msg->time_increment = 0.0;
scan_msg->scan_time = 1.0 / frequency_;
scan_msg->range_min = 0.1; // 最小检测距离10厘米
scan_msg->range_max = 30.0; // 最大检测距离30米
// 存储激光点数量
size_t num_points = static_cast<size_t>((scan_msg->angle_max - scan_msg->angle_min) / scan_msg->angle_increment) + 1;
scan_msg->ranges.resize(num_points);
scan_msg->intensities.resize(num_points);
while (running_ && rclcpp::ok()) {
try {
// 读取LIDAR数据根据具体LIDAR协议解析
if (serial_port_.available()) {
std::string data = serial_port_.read(serial_port_.available());
// 解析数据示例实际需根据LIDAR协议实现
if (parseLidarData(data, scan_msg->ranges, scan_msg->intensities)) {
// 设置时间戳
scan_msg->header.stamp = this->now();
// 发布激光扫描消息
scan_pub_->publish(std::move(scan_msg));
// 为下一次发布创建新消息
scan_msg = std::make_unique<sensor_msgs::msg::LaserScan>();
scan_msg->header.frame_id = frame_id_;
scan_msg->angle_min = -3.141592;
scan_msg->angle_max = 3.141592;
scan_msg->angle_increment = 0.0174533;
scan_msg->time_increment = 0.0;
scan_msg->scan_time = 1.0 / frequency_;
scan_msg->range_min = 0.1;
scan_msg->range_max = 30.0;
scan_msg->ranges.resize(num_points);
scan_msg->intensities.resize(num_points);
}
}
} catch (serial::SerialException& e) {
RCLCPP_ERROR(this->get_logger(), "Serial exception during data collection: %s", e.what());
running_ = false;
}
rate.sleep();
}
}
bool parseLidarData(const std::string& data, std::vector<float>& ranges, std::vector<float>& intensities)
{
// 这里需要根据具体LIDAR的数据协议实现解析逻辑
// 示例代码仅作为框架,实际应用中需要替换为真实的解析逻辑
// 简单示例:假设数据格式为"RANGE1,INTENSITY1;RANGE2,INTENSITY2;..."
// 注意这只是一个示例实际LIDAR协议会复杂得多
size_t pos = 0;
size_t point_index = 0;
std::string token;
while ((pos = data.find(';')) != std::string::npos && point_index < ranges.size()) {
token = data.substr(0, pos);
// 解析距离和强度
size_t comma_pos = token.find(',');
if (comma_pos != std::string::npos) {
try {
float range = std::stof(token.substr(0, comma_pos));
float intensity = std::stof(token.substr(comma_pos + 1));
// 检查范围
if (range >= 0.1 && range <= 30.0) {
ranges[point_index] = range;
intensities[point_index] = intensity;
} else {
ranges[point_index] = std::numeric_limits<float>::infinity();
intensities[point_index] = 0.0;
}
} catch (const std::invalid_argument&) {
// 解析失败,设置为无穷大
ranges[point_index] = std::numeric_limits<float>::infinity();
intensities[point_index] = 0.0;
}
}
data.erase(0, pos + 1);
point_index++;
}
// 如果解析到了足够的点返回true
return point_index > ranges.size() / 2;
}
// 串口通信对象
serial::Serial serial_port_;
// 参数
std::string port_;
int baudrate_;
std::string frame_id_;
double frequency_;
// 发布者
rclcpp::Publisher<sensor_msgs::msg::LaserScan>::SharedPtr scan_pub_;
// 数据采集线程
std::thread data_thread_;
bool running_{false};
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<LidarDriverNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}

View File

@@ -1,75 +0,0 @@
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "sensor_msgs/msg/joint_state.hpp"
class MotorDriverNode : public rclcpp::Node
{
public:
MotorDriverNode() : Node("motor_driver_node")
{
// 订阅速度命令
cmd_vel_sub_ = this->create_subscription<geometry_msgs::msg::Twist>(
"cmd_vel", 10, std::bind(&MotorDriverNode::cmdVelCallback, this, std::placeholders::_1));
// 发布关节状态
joint_state_pub_ = this->create_publisher<sensor_msgs::msg::JointState>(
"joint_states", 10);
// 初始化硬件接口
initHardwareInterface();
// 创建定时器,定期发布关节状态
timer_ = this->create_wall_timer(
std::chrono::milliseconds(50), std::bind(&MotorDriverNode::publishJointStates, this));
}
private:
void cmdVelCallback(const geometry_msgs::msg::Twist::SharedPtr msg)
{
// 将ROS速度命令转换为电机PWM值
double linear = msg->linear.x;
double angular = msg->angular.z;
// 控制电机
setMotorSpeeds(linear, angular);
}
void publishJointStates()
{
// 读取电机编码器值
auto joint_state = std::make_unique<sensor_msgs::msg::JointState>();
joint_state->header.stamp = this->now();
// 填充关节状态数据
// ...
joint_state_pub_->publish(std::move(joint_state));
}
void initHardwareInterface()
{
// 初始化电机控制器接口
// ...
}
void setMotorSpeeds(double linear, double angular)
{
// 实现差速驱动转换
// ...
// 发送PWM信号到电机
// ...
}
rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr cmd_vel_sub_;
rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr joint_state_pub_;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<MotorDriverNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}

View File

@@ -1,25 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(interaction_pkg)
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(rclpy REQUIRED)
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()

View File

@@ -1,21 +0,0 @@
<?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>interaction_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ray@todo.todo">ray</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclpy</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,133 +0,0 @@
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import BatteryState, LaserScan
from nav_msgs.msg import Odometry
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
class RobotMonitor(Node):
def __init__(self):
super().__init__('robot_monitor')
# 订阅电池状态
self.battery_sub = self.create_subscription(
BatteryState, 'battery_state', self.battery_callback, 10)
# 订阅激光扫描
self.laser_sub = self.create_subscription(
LaserScan, 'scan', self.laser_callback, 10)
# 订阅里程计
self.odom_sub = self.create_subscription(
Odometry, 'odom', self.odom_callback, 10)
# 初始化数据
self.battery_voltage = 0.0
self.laser_ranges = []
self.robot_x = 0.0
self.robot_y = 0.0
# 创建图形
self.fig, (self.ax1, self.ax2, self.ax3) = plt.subplots(3, 1, figsize=(10, 15))
self.fig.suptitle('Robot Status Monitor')
# 设置电池电量图
self.battery_bar = self.ax1.bar(['Battery'], [0], color='green')
self.ax1.set_ylim(0, 100)
self.ax1.set_ylabel('Battery Level (%)')
# 设置激光扫描图
self.laser_line, = self.ax2.plot([], [], 'b-')
self.ax2.set_xlim(-10, 10)
self.ax2.set_ylim(-10, 10)
self.ax2.set_aspect('equal')
self.ax2.set_title('Laser Scan')
# 设置轨迹图
self.trajectory_line, = self.ax3.plot([], [], 'r-')
self.robot_marker, = self.ax3.plot([], [], 'bo', markersize=10)
self.ax3.set_xlim(-5, 5)
self.ax3.set_ylim(-5, 5)
self.ax3.set_aspect('equal')
self.ax3.set_title('Robot Trajectory')
# 轨迹数据
self.trajectory_x = []
self.trajectory_y = []
# 创建动画
self.ani = FuncAnimation(self.fig, self.update_plot, interval=100)
self.get_logger().info('Robot monitor initialized')
def battery_callback(self, msg):
# 假设电压范围为10.0-12.6V
voltage = msg.voltage
self.battery_voltage = max(0.0, min(100.0, (voltage - 10.0) / (12.6 - 10.0) * 100.0))
def laser_callback(self, msg):
self.laser_ranges = msg.ranges
self.laser_angles = np.linspace(msg.angle_min, msg.angle_max, len(msg.ranges))
def odom_callback(self, msg):
self.robot_x = msg.pose.pose.position.x
self.robot_y = msg.pose.pose.position.y
self.trajectory_x.append(self.robot_x)
self.trajectory_y.append(self.robot_y)
# 限制轨迹长度
if len(self.trajectory_x) > 1000:
self.trajectory_x = self.trajectory_x[-1000:]
self.trajectory_y = self.trajectory_y[-1000:]
def update_plot(self, frame):
# 更新电池图
self.battery_bar[0].set_height(self.battery_voltage)
if self.battery_voltage < 20:
self.battery_bar[0].set_color('red')
elif self.battery_voltage < 50:
self.battery_bar[0].set_color('yellow')
else:
self.battery_bar[0].set_color('green')
# 更新激光图
if len(self.laser_ranges) > 0:
x = np.array(self.laser_ranges) * np.cos(self.laser_angles)
y = np.array(self.laser_ranges) * np.sin(self.laser_angles)
self.laser_line.set_data(x, y)
# 更新轨迹图
self.trajectory_line.set_data(self.trajectory_x, self.trajectory_y)
self.robot_marker.set_data(self.robot_x, self.robot_y)
# 调整坐标轴范围以适应轨迹
if len(self.trajectory_x) > 0:
x_min, x_max = min(self.trajectory_x), max(self.trajectory_x)
y_min, y_max = min(self.trajectory_y), max(self.trajectory_y)
margin = 1.0
self.ax3.set_xlim(min(x_min - margin, -5), max(x_max + margin, 5))
self.ax3.set_ylim(min(y_min - margin, -5), max(y_max + margin, 5))
return self.battery_bar, self.laser_line, self.trajectory_line, self.robot_marker
def main(args=None):
rclpy.init(args=args)
node = RobotMonitor()
# 在单独的线程中运行ROS节点
import threading
thread = threading.Thread(target=rclpy.spin, args=(node,), daemon=True)
thread.start()
# 显示图形
plt.tight_layout()
plt.show()
# 关闭节点
node.destroy_node()
rclpy.shutdown()
thread.join()
if __name__ == '__main__':
main()

View File

@@ -1,65 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(preception_pkg)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 设置CMake策略以处理_ROOT变量
cmake_policy(SET CMP0074 NEW)
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(OpenCV REQUIRED)
# 查找PCL和ROS2 PCL包
find_package(PCL REQUIRED COMPONENTS common filters features io segmentation surface)
find_package(pcl_conversions REQUIRED)
find_package(pcl_ros REQUIRED)
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()
# 添加C++组件
add_executable(point_cloud_processor src/point_cloud_processor.cpp)
ament_target_dependencies(point_cloud_processor
rclcpp
sensor_msgs
pcl_conversions
pcl_ros
)
# 链接PCL库
target_link_libraries(point_cloud_processor
${PCL_LIBRARIES}
)
# 安装C++可执行文件
install(TARGETS
point_cloud_processor
DESTINATION lib/${PROJECT_NAME})
# 安装Python脚本
install(PROGRAMS
scripts/object_detection_node.py
DESTINATION lib/${PROJECT_NAME}
)
# 安装其他资源
install(DIRECTORY
models
DESTINATION share/${PROJECT_NAME}
)
ament_package()

View File

@@ -1,25 +0,0 @@
<?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>preception_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ray@todo.todo">ray</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclpy</depend>
<depend>sensor_msgs</depend>
<depend>pcl_conversions</depend>
<depend>pcl_ros</depend>
<depend>PCL</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,91 +0,0 @@
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
import numpy as np
import tensorflow as tf
class ObjectDetectionNode(Node):
def __init__(self):
super().__init__('object_detection_node')
# 创建图像订阅者
self.image_sub = self.create_subscription(
Image, 'camera/image_raw', self.image_callback, 10)
# 创建检测结果发布者
self.detection_pub = self.create_publisher(Image, 'detection_image', 10)
# 初始化OpenCV桥接器
self.bridge = CvBridge()
# 加载TensorFlow模型
self.model = tf.saved_model.load('path/to/saved_model')
# 获取类别名称
self.class_names = ['person', 'bicycle', 'car', ...] # 根据实际模型调整
self.get_logger().info('Object detection node initialized')
def image_callback(self, msg):
try:
# 将ROS图像消息转换为OpenCV格式
cv_image = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
# 预处理图像
input_tensor = tf.convert_to_tensor(cv_image)
input_tensor = input_tensor[tf.newaxis, ...]
# 运行目标检测
detections = self.model(input_tensor)
# 处理检测结果
num_detections = int(detections.pop('num_detections'))
detections = {key: value[0, :num_detections].numpy()
for key, value in detections.items()}
detections['num_detections'] = num_detections
# 过滤置信度低的检测结果
detections['detection_classes'] = detections['detection_classes'].astype(np.int64)
scores = detections['detection_scores']
classes = detections['detection_classes']
boxes = detections['detection_boxes']
# 在图像上绘制检测结果
for i in range(len(scores)):
if scores[i] > 0.5: # 置信度阈值
class_id = classes[i]
class_name = self.class_names[class_id]
box = boxes[i]
# 转换边界框坐标
h, w, _ = cv_image.shape
ymin, xmin, ymax, xmax = box
xmin = int(xmin * w)
xmax = int(xmax * w)
ymin = int(ymin * h)
ymax = int(ymax * h)
# 绘制边界框和标签
cv2.rectangle(cv_image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)
cv2.putText(cv_image, f'{class_name}: {scores[i]:.2f}',
(xmin, ymin - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 发布结果图像
output_msg = self.bridge.cv2_to_imgmsg(cv_image, 'bgr8')
output_msg.header = msg.header
self.detection_pub.publish(output_msg)
except Exception as e:
self.get_logger().error(f'Error processing image: {str(e)}')
def main(args=None):
rclpy.init(args=args)
node = ObjectDetectionNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()

View File

@@ -1,52 +0,0 @@
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/point_cloud2.hpp"
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/filters/voxel_grid.h>
class PointCloudProcessor : public rclcpp::Node
{
public:
PointCloudProcessor() : Node("point_cloud_processor")
{
// 订阅原始点云
cloud_sub_ = this->create_subscription<sensor_msgs::msg::PointCloud2>(
"raw_cloud", 10, std::bind(&PointCloudProcessor::cloudCallback, this, std::placeholders::_1));
// 发布处理后的点云
filtered_cloud_pub_ = this->create_publisher<sensor_msgs::msg::PointCloud2>(
"filtered_cloud", 10);
}
private:
void cloudCallback(const sensor_msgs::msg::PointCloud2::SharedPtr msg)
{
// 转换为PCL格式
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::fromROSMsg(*msg, *cloud);
// 应用体素网格滤波降采样
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::VoxelGrid<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setLeafSize(0.01f, 0.01f, 0.01f);
sor.filter(*cloud_filtered);
// 转换回ROS格式并发布
sensor_msgs::msg::PointCloud2 output;
pcl::toROSMsg(*cloud_filtered, output);
output.header = msg->header;
filtered_cloud_pub_->publish(output);
}
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr cloud_sub_;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr filtered_cloud_pub_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<PointCloudProcessor>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}

View File

@@ -0,0 +1,69 @@
cmake_minimum_required(VERSION 3.10)
project(rm_arm_control)
# 设置 C 标准
set(CMAKE_C_STANDARD 11)
# 添加忽略定义重定义警告的选项
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Wno-define-redefinition)
endif()
# 依赖
find_package(ament_cmake REQUIRED)
find_package(Eigen3 REQUIRED) # 查找Eigen3库
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(trajectory_msgs REQUIRED)
find_package(control_msgs REQUIRED)
include_directories(
include
${EIGEN3_INCLUDE_DIRS} # 引入Eigen3的头文件路径
)
# 设置源文件
set(SOURCES
src/rm_arm_handler.cpp
src/rm_arm_node.cpp
)
# 添加可执行文件
add_executable(rm_arm_control ${SOURCES})
ament_target_dependencies(rm_arm_control
rclcpp
geometry_msgs
sensor_msgs
trajectory_msgs
control_msgs
)
# 查找库
find_library(RMAN_API_LIB NAMES api_cpp PATHS "${CMAKE_CURRENT_SOURCE_DIR}/Robotic_Arm/lib")
# 链接库
target_link_libraries(rm_arm_control ${RMAN_API_LIB})
# 包含目录
target_include_directories(rm_arm_control PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Robotic_Arm/include")
# 如果是 UNIX 平台且不是 ARM 平台,确保动态库文件可以在运行时被找到(这一步通常不是必须的,但在某些配置下可能需要)
if(UNIX)
add_custom_command(TARGET rm_arm_control POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/Robotic_Arm/lib/libapi_cpp.so"
$<TARGET_FILE_DIR:rm_arm_control>)
endif()
install(TARGETS
rm_arm_control
DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY launch
DESTINATION share/${PROJECT_NAME}/
)
ament_package()

View File

@@ -0,0 +1,13 @@
Copyright 2024 realman-robotics
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.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
#ifndef RM_INTERFACE_GLOBAL_H
#define RM_INTERFACE_GLOBAL_H
#ifdef __linux
#define RM_INTERFACE_EXPORT
#endif
#if _WIN32
#if defined(RM_INTERFACE_LIBRARY)
# define RM_INTERFACE_EXPORT __declspec(dllexport)
#else
# define RM_INTERFACE_EXPORT __declspec(dllexport)
#endif
#endif
#endif // RM_INTERFACE_GLOBAL_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
#ifndef RM_VERSION_H
#define RM_VERSION_H
#ifdef __cplusplus
extern "C" {
#endif
#define SDK_VERSION ("1.1.1")
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
[20240708 17:56:47] Begin API Testing.
[20240708 18:05:04] Begin API Testing.
[20240708 18:08:31] Begin API Testing.
[20240708 18:33:17] Begin API Testing.
[20240708 19:21:46] Begin API Testing.
[20240708 19:59:38] Begin API Testing.
[20240710 14:04:26] Begin API Testing.
[20240710 14:07:27] Begin API Testing.
[20240710 14:17:49] Begin API Testing.
[20240710 14:22:08] Begin API Testing.
[20240710 14:31:25] Begin API Testing.
[20240710 18:16:24] Begin API Testing.
[20240711 08:01:43] Begin API Testing.
[20240711 09:44:12] Begin API Testing.
[20240711 10:21:41] Begin API Testing.
[20240711 10:30:04] Begin API Testing.
[20240711 10:30:38] Begin API Testing.
[20240711 14:53:33] Begin API Testing.
[20240711 19:00:44] Begin API Testing.
[20240711 19:03:09] Begin API Testing.
[20240711 19:05:39] Begin API Testing.
[20240712 09:52:46] Begin API Testing.
[20240712 17:47:12] Begin API Testing.
[20240712 17:47:50] Begin API Testing.
[20240712 17:50:39] Begin API Testing.
[20240712 17:55:20] Begin API Testing.
[20240713 18:14:24] Begin API Testing.
[20240713 18:17:21] Begin API Testing.
[20240713 18:18:33] Begin API Testing.

View File

@@ -0,0 +1,582 @@
{"point":[-81,-16537,15047,-5480,-17,17705,-17]}
{"point":[-83,-16537,15048,-5484,-12,17707,-17]}
{"point":[-82,-16537,15054,-5480,-11,17703,-19]}
{"point":[-85,-16539,15059,-5483,-11,17699,-20]}
{"point":[-85,-16541,15063,-5484,-11,17690,-21]}
{"point":[-85,-16542,15069,-5486,-16,17663,-21]}
{"point":[-86,-16541,15076,-5484,-19,17649,-21]}
{"point":[-85,-16539,15080,-5486,-22,17633,-23]}
{"point":[-85,-16541,15091,-5487,-31,17608,-21]}
{"point":[-85,-16541,15096,-5487,-33,17564,-23]}
{"point":[-86,-16542,15099,-5487,-37,17543,-23]}
{"point":[-86,-16541,15109,-5491,-39,17523,-23]}
{"point":[-85,-16542,15111,-5493,-38,17490,-23]}
{"point":[-86,-16542,15114,-5495,-38,17473,-23]}
{"point":[-86,-16544,15118,-5500,-41,17457,-23]}
{"point":[-85,-16544,15118,-5502,-39,17438,-24]}
{"point":[-85,-16545,15118,-5504,-38,17394,-21]}
{"point":[-83,-16544,15118,-5511,-35,17368,-23]}
{"point":[-86,-16544,15118,-5516,-35,17337,-21]}
{"point":[-85,-16544,15120,-5520,-34,17284,-23]}
{"point":[-85,-16544,15118,-5520,-33,17259,-23]}
{"point":[-86,-16544,15117,-5523,-30,17233,-23]}
{"point":[-86,-16544,15117,-5523,-15,17173,-23]}
{"point":[-85,-16545,15115,-5523,-13,17135,-24]}
{"point":[-85,-16544,15110,-5522,-12,17094,-21]}
{"point":[-86,-16545,15103,-5522,-13,17056,-23]}
{"point":[-85,-16545,15096,-5523,-16,16987,-24]}
{"point":[-86,-16546,15087,-5524,-19,16954,-23]}
{"point":[-85,-16548,15071,-5526,-23,16914,-23]}
{"point":[-85,-16549,15060,-5526,-28,16846,-23]}
{"point":[-86,-16549,15040,-5526,-31,16807,-23]}
{"point":[-86,-16550,15028,-5528,-35,16770,-23]}
{"point":[-86,-16550,15019,-5530,-39,16728,-23]}
{"point":[-86,-16552,15011,-5530,-38,16636,-24]}
{"point":[-85,-16555,14995,-5530,-41,16593,-23]}
{"point":[-85,-16553,14988,-5534,-42,16556,-23]}
{"point":[-86,-16555,14978,-5534,-43,16476,-24]}
{"point":[-85,-16555,14953,-5537,-42,16435,-24]}
{"point":[-86,-16553,14945,-5537,-43,16397,-23]}
{"point":[-85,-16556,14929,-5539,-43,16316,-24]}
{"point":[-86,-16555,14911,-5538,-46,16278,-24]}
{"point":[-85,-16557,14870,-5541,-45,16232,-26]}
{"point":[-85,-16559,14849,-5544,-45,16141,-23]}
{"point":[-85,-16557,14824,-5542,-45,16090,-23]}
{"point":[-85,-16560,14779,-5544,-46,16044,-24]}
{"point":[-86,-16560,14761,-5544,-45,16004,-24]}
{"point":[-86,-16561,14751,-5545,-45,15911,-23]}
{"point":[-85,-16561,14731,-5545,-43,15862,-24]}
{"point":[-86,-16564,14724,-5546,-45,15802,-23]}
{"point":[-86,-16563,14723,-5549,-45,15695,-26]}
{"point":[-86,-16567,14714,-5549,-45,15632,-24]}
{"point":[-86,-16567,14712,-5548,-48,15558,-26]}
{"point":[-86,-16568,14710,-5549,-43,15435,-24]}
{"point":[-86,-16568,14709,-5548,-45,15358,-24]}
{"point":[-85,-16572,14710,-5549,-46,15286,-26]}
{"point":[-85,-16575,14710,-5550,-49,15126,-24]}
{"point":[-86,-16578,14710,-5553,-48,15044,-24]}
{"point":[-85,-16583,14710,-5556,-49,14960,-26]}
{"point":[-87,-16585,14710,-5560,-48,14861,-26]}
{"point":[-85,-16589,14712,-5564,-50,14702,-26]}
{"point":[-86,-16593,14712,-5567,-50,14615,-26]}
{"point":[-85,-16601,14710,-5575,-50,14507,-27]}
{"point":[-83,-16603,14712,-5579,-50,14422,-27]}
{"point":[-83,-16610,14712,-5587,-52,14224,-27]}
{"point":[-85,-16614,14712,-5593,-53,14143,-26]}
{"point":[-85,-16622,14710,-5612,-54,14041,-27]}
{"point":[-85,-16625,14709,-5623,-57,13840,-27]}
{"point":[-86,-16629,14710,-5637,-59,13730,-28]}
{"point":[-86,-16636,14710,-5664,-59,13614,-28]}
{"point":[-86,-16637,14710,-5679,-60,13476,-27]}
{"point":[-87,-16641,14710,-5704,-59,13241,-26]}
{"point":[-86,-16647,14709,-5754,-59,13094,-27]}
{"point":[-85,-16648,14709,-5776,-63,12977,-28]}
{"point":[-86,-16652,14710,-5799,-63,12696,-26]}
{"point":[-85,-16656,14712,-5824,-63,12578,-28]}
{"point":[-83,-16664,14712,-5880,-63,12436,-27]}
{"point":[-82,-16673,14712,-5905,-65,12201,-27]}
{"point":[-79,-16677,14710,-5936,-67,12076,-27]}
{"point":[-76,-16695,14713,-5995,-67,11962,-27]}
{"point":[-70,-16706,14712,-6024,-70,11846,-28]}
{"point":[-64,-16717,14710,-6050,-65,11587,-28]}
{"point":[-65,-16741,14710,-6113,-68,11458,-30]}
{"point":[-64,-16756,14710,-6149,-71,11344,-28]}
{"point":[-64,-16770,14712,-6183,-72,11061,-28]}
{"point":[-61,-16783,14710,-6212,-72,10931,-28]}
{"point":[-65,-16807,14712,-6274,-74,10811,-28]}
{"point":[-65,-16820,14713,-6310,-71,10509,-28]}
{"point":[-68,-16835,14713,-6344,-75,10372,-30]}
{"point":[-64,-16865,14712,-6411,-75,10251,-28]}
{"point":[-64,-16880,14710,-6454,-74,10092,-28]}
{"point":[-64,-16898,14712,-6490,-72,9806,-28]}
{"point":[-65,-16917,14712,-6534,-75,9684,-28]}
{"point":[-59,-16958,14710,-6619,-74,9559,-30]}
{"point":[-57,-16976,14709,-6660,-70,9283,-28]}
{"point":[-60,-16993,14710,-6705,-71,9137,-28]}
{"point":[-63,-17027,14710,-6803,-72,8985,-28]}
{"point":[-61,-17048,14712,-6852,-72,8834,-28]}
{"point":[-61,-17065,14710,-6906,-70,8544,-27]}
{"point":[-63,-17086,14709,-6965,-72,8412,-28]}
{"point":[-65,-17129,14709,-7087,-71,8243,-28]}
{"point":[-60,-17152,14710,-7154,-76,8098,-28]}
{"point":[-56,-17174,14710,-7224,-75,7845,-28]}
{"point":[-59,-17222,14713,-7355,-72,7700,-27]}
{"point":[-59,-17251,14712,-7425,-72,7454,-30]}
{"point":[-60,-17280,14712,-7498,-72,7323,-28]}
{"point":[-53,-17306,14712,-7570,-74,7196,-28]}
{"point":[-46,-17366,14712,-7727,-74,7073,-28]}
{"point":[-41,-17396,14712,-7807,-74,6796,-30]}
{"point":[-33,-17425,14713,-7882,-74,6796,-28]}
{"point":[-24,-17484,14712,-8042,-72,6514,-28]}
{"point":[-27,-17516,14712,-8118,-74,6370,-28]}
{"point":[-23,-17556,14713,-8191,-75,6076,-28]}
{"point":[-20,-17624,14713,-8260,-76,5929,-30]}
{"point":[-19,-17663,14712,-8410,-74,5802,-30]}
{"point":[-20,-17699,14712,-8492,-74,5533,-30]}
{"point":[-20,-17734,14713,-8581,-76,5399,-30]}
{"point":[-15,-17811,14713,-8747,-79,5265,-28]}
{"point":[-11,-17851,14714,-8828,-76,5144,-28]}
{"point":[-12,-17892,14713,-8911,-72,4912,-28]}
{"point":[-15,-17970,14712,-9085,-72,4753,-28]}
{"point":[-15,-18014,14712,-9168,-75,4615,-28]}
{"point":[-13,-18056,14710,-9245,-75,4356,-28]}
{"point":[-15,-18139,14709,-9404,-72,4224,-28]}
{"point":[-19,-18181,14710,-9497,-72,4093,-28]}
{"point":[-19,-18219,14712,-9580,-78,3838,-28]}
{"point":[-13,-18307,14714,-9688,-76,3696,-28]}
{"point":[-13,-18355,14712,-9884,-76,3582,-30]}
{"point":[-15,-18403,14713,-9976,-76,3477,-28]}
{"point":[-16,-18452,14714,-10059,-76,3230,-30]}
{"point":[-11,-18547,14716,-10259,-78,3118,-28]}
{"point":[-6,-18597,14714,-10356,-76,3006,-30]}
{"point":[-5,-18645,14716,-10445,-79,2808,-30]}
{"point":[-6,-18696,14716,-10535,-74,2702,-28]}
{"point":[-4,-18792,14713,-10732,-78,2579,-30]}
{"point":[-1,-18842,14713,-10825,-78,2447,-30]}
{"point":[-1,-18893,14714,-10924,-76,2231,-28]}
{"point":[-1,-18994,14713,-11112,-76,2121,-27]}
{"point":[1,-19046,14710,-11207,-71,2006,-30]}
{"point":[-1,-19097,14712,-11307,-72,1772,-30]}
{"point":[0,-19201,14713,-11515,-74,1685,-30]}
{"point":[0,-19246,14713,-11624,-72,1555,-30]}
{"point":[-4,-19293,14714,-11737,-78,1369,-28]}
{"point":[-4,-19400,14714,-11843,-75,1263,-28]}
{"point":[-5,-19452,14714,-12063,-74,1160,-30]}
{"point":[-4,-19506,14714,-12175,-76,976,-30]}
{"point":[1,-19620,14717,-12391,-76,885,-28]}
{"point":[2,-19675,14716,-12503,-78,808,-30]}
{"point":[5,-19731,14716,-12610,-76,638,-28]}
{"point":[5,-19793,14717,-12718,-78,554,-28]}
{"point":[5,-19910,14716,-12935,-75,472,-30]}
{"point":[5,-19966,14716,-13031,-78,293,-28]}
{"point":[5,-20026,14716,-13127,-74,210,-28]}
{"point":[5,-20147,14716,-13320,-74,123,-28]}
{"point":[4,-20208,14714,-13412,-74,42,-28]}
{"point":[8,-20264,14714,-13506,-75,-114,-30]}
{"point":[8,-20322,14714,-13610,-75,-184,-28]}
{"point":[5,-20433,14714,-13871,-71,-255,-28]}
{"point":[1,-20489,14716,-14011,-72,-414,-28]}
{"point":[2,-20547,14714,-14125,-71,-498,-30]}
{"point":[0,-20658,14713,-14338,-72,-565,-28]}
{"point":[-1,-20720,14713,-14442,-71,-644,-31]}
{"point":[0,-20779,14714,-14551,-70,-788,-28]}
{"point":[1,-20898,14716,-14772,-68,-852,-30]}
{"point":[5,-20961,14716,-14875,-68,-911,-30]}
{"point":[5,-21021,14717,-14979,-68,-1047,-30]}
{"point":[4,-21144,14717,-15091,-71,-1126,-28]}
{"point":[5,-21209,14717,-15309,-70,-1182,-30]}
{"point":[8,-21273,14719,-15419,-70,-1240,-28]}
{"point":[5,-21335,14717,-15527,-68,-1354,-30]}
{"point":[5,-21468,14714,-15751,-67,-1403,-27]}
{"point":[5,-21531,14716,-15868,-67,-1487,-30]}
{"point":[6,-21596,14717,-15989,-65,-1540,-30]}
{"point":[5,-21659,14714,-16116,-67,-1582,-28]}
{"point":[6,-21781,14719,-16351,-64,-1621,-28]}
{"point":[6,-21846,14719,-16472,-70,-1694,-28]}
{"point":[6,-21976,14716,-16588,-65,-1737,-30]}
{"point":[5,-22044,14717,-16817,-67,-1775,-30]}
{"point":[5,-22105,14719,-16934,-74,-1855,-28]}
{"point":[5,-22239,14719,-17135,-74,-1899,-30]}
{"point":[6,-22305,14719,-17252,-75,-1945,-28]}
{"point":[6,-22372,14717,-17357,-75,-2031,-30]}
{"point":[5,-22439,14716,-17462,-75,-2076,-30]}
{"point":[6,-22576,14716,-17682,-75,-2112,-30]}
{"point":[5,-22644,14716,-17791,-74,-2160,-28]}
{"point":[5,-22712,14716,-17899,-70,-2239,-30]}
{"point":[6,-22847,14717,-18135,-72,-2279,-28]}
{"point":[5,-22916,14717,-18246,-72,-2315,-28]}
{"point":[6,-22982,14716,-18347,-72,-2371,-28]}
{"point":[6,-23049,14717,-18444,-76,-2410,-28]}
{"point":[5,-23179,14717,-18674,-75,-2443,-30]}
{"point":[6,-23247,14717,-18800,-75,-2503,-30]}
{"point":[8,-23314,14717,-18923,-78,-2539,-30]}
{"point":[6,-23462,14719,-19160,-78,-2568,-28]}
{"point":[6,-23521,14720,-19278,-79,-2602,-30]}
{"point":[6,-23590,14721,-19388,-78,-2675,-30]}
{"point":[6,-23734,14720,-19606,-79,-2713,-30]}
{"point":[8,-23810,14719,-19712,-78,-2746,-30]}
{"point":[6,-23882,14721,-19819,-81,-2816,-28]}
{"point":[6,-24020,14719,-20033,-78,-2856,-30]}
{"point":[8,-24091,14719,-20139,-75,-2923,-30]}
{"point":[6,-24153,14720,-20254,-76,-2964,-30]}
{"point":[5,-24285,14719,-20367,-78,-2989,-30]}
{"point":[6,-24351,14716,-20591,-74,-3013,-30]}
{"point":[6,-24415,14714,-20705,-75,-3067,-28]}
{"point":[6,-24550,14716,-20815,-75,-3088,-28]}
{"point":[6,-24610,14717,-21034,-75,-3116,-28]}
{"point":[6,-24673,14720,-21143,-79,-3179,-30]}
{"point":[5,-24739,14720,-21248,-76,-3202,-28]}
{"point":[5,-24873,14720,-21467,-79,-3228,-30]}
{"point":[6,-24937,14719,-21564,-79,-3251,-30]}
{"point":[6,-25013,14717,-21669,-78,-3301,-30]}
{"point":[6,-25143,14720,-21884,-78,-3337,-30]}
{"point":[5,-25223,14723,-21983,-76,-3360,-30]}
{"point":[6,-25280,14728,-22085,-76,-3412,-28]}
{"point":[9,-25348,14735,-22181,-76,-3431,-28]}
{"point":[8,-25481,14730,-22377,-72,-3449,-28]}
{"point":[9,-25547,14723,-22478,-74,-3479,-28]}
{"point":[8,-25617,14720,-22572,-75,-3496,-28]}
{"point":[8,-25741,14717,-22788,-75,-3511,-27]}
{"point":[8,-25804,14719,-22909,-72,-3526,-30]}
{"point":[6,-25861,14721,-23026,-74,-3551,-30]}
{"point":[8,-25920,14719,-23135,-74,-3593,-30]}
{"point":[4,-26033,14717,-23329,-74,-3614,-28]}
{"point":[2,-26095,14717,-23421,-74,-3640,-30]}
{"point":[0,-26148,14717,-23512,-72,-3648,-28]}
{"point":[-2,-26268,14717,-23611,-72,-3651,-28]}
{"point":[-2,-26328,14717,-23789,-70,-3654,-28]}
{"point":[-1,-26385,14719,-23871,-68,-3654,-30]}
{"point":[0,-26438,14717,-23962,-68,-3655,-28]}
{"point":[1,-26555,14719,-24128,-65,-3655,-28]}
{"point":[4,-26619,14719,-24212,-68,-3655,-28]}
{"point":[4,-26666,14719,-24294,-63,-3657,-28]}
{"point":[5,-26775,14716,-24451,-60,-3655,-28]}
{"point":[5,-26828,14717,-24529,-61,-3657,-28]}
{"point":[6,-26890,14717,-24603,-57,-3655,-28]}
{"point":[6,-26986,14720,-24742,-59,-3657,-28]}
{"point":[6,-27036,14717,-24812,-54,-3655,-28]}
{"point":[6,-27082,14717,-24878,-54,-3657,-28]}
{"point":[5,-27129,14717,-24949,-53,-3655,-28]}
{"point":[0,-27218,14717,-25094,-56,-3655,-27]}
{"point":[0,-27262,14717,-25157,-54,-3657,-30]}
{"point":[1,-27305,14716,-25220,-54,-3655,-28]}
{"point":[-5,-27383,14714,-25349,-52,-3654,-28]}
{"point":[-5,-27426,14716,-25411,-52,-3655,-28]}
{"point":[-9,-27463,14716,-25471,-53,-3655,-28]}
{"point":[-9,-27494,14717,-25532,-53,-3657,-28]}
{"point":[-15,-27566,14716,-25638,-54,-3657,-30]}
{"point":[-20,-27592,14716,-25690,-53,-3655,-28]}
{"point":[-28,-27619,14716,-25742,-53,-3654,-28]}
{"point":[-31,-27670,14716,-25838,-50,-3657,-28]}
{"point":[-31,-27693,14716,-25881,-52,-3657,-28]}
{"point":[-34,-27714,14713,-25923,-49,-3654,-28]}
{"point":[-45,-27752,14714,-25990,-49,-3654,-28]}
{"point":[-56,-27773,14713,-26015,-48,-3655,-28]}
{"point":[-60,-27788,14714,-26039,-49,-3654,-28]}
{"point":[-71,-27810,14713,-26071,-46,-3654,-28]}
{"point":[-78,-27822,14712,-26084,-42,-3653,-28]}
{"point":[-85,-27828,14712,-26098,-46,-3653,-28]}
{"point":[-89,-27839,14710,-26107,-46,-3651,-28]}
{"point":[-89,-27842,14709,-26122,-43,-3651,-28]}
{"point":[-90,-27844,14708,-26125,-45,-3647,-27]}
{"point":[-90,-27844,14706,-26126,-43,-3646,-27]}
{"point":[-92,-27847,14703,-26125,-42,-3643,-28]}
{"point":[-93,-27844,14703,-26125,-41,-3633,-28]}
{"point":[-92,-27843,14702,-26122,-41,-3628,-28]}
{"point":[-93,-27840,14702,-26122,-39,-3621,-27]}
{"point":[-93,-27842,14702,-26120,-35,-3607,-28]}
{"point":[-93,-27839,14702,-26117,-26,-3574,-28]}
{"point":[-90,-27840,14703,-26115,-20,-3548,-28]}
{"point":[-87,-27838,14703,-26114,-9,-3511,-27]}
{"point":[-89,-27838,14703,-26113,-9,-3412,-27]}
{"point":[-90,-27839,14702,-26111,-6,-3352,-27]}
{"point":[-90,-27838,14702,-26106,-6,-3295,-28]}
{"point":[-93,-27838,14702,-26102,-5,-3175,-27]}
{"point":[-90,-27839,14703,-26096,-6,-3103,-28]}
{"point":[-92,-27838,14702,-26085,-8,-3033,-28]}
{"point":[-92,-27836,14701,-26074,-5,-2958,-27]}
{"point":[-92,-27838,14701,-26062,-8,-2789,-28]}
{"point":[-93,-27835,14701,-26037,-6,-2697,-27]}
{"point":[-92,-27835,14701,-26021,-8,-2500,-28]}
{"point":[-93,-27835,14702,-25997,-6,-2400,-28]}
{"point":[-92,-27836,14699,-25971,-8,-2308,-28]}
{"point":[-92,-27839,14701,-25901,-8,-2208,-30]}
{"point":[-92,-27836,14701,-25852,-6,-2007,-28]}
{"point":[-87,-27839,14703,-25771,-8,-1897,-27]}
{"point":[-86,-27839,14705,-25721,-8,-1771,-28]}
{"point":[-85,-27839,14705,-25666,-9,-1507,-27]}
{"point":[-86,-27838,14702,-25605,-8,-1355,-27]}
{"point":[-86,-27838,14703,-25499,-8,-1196,-27]}
{"point":[-87,-27836,14703,-25433,-16,-863,-27]}
{"point":[-86,-27836,14701,-25355,-17,-701,-28]}
{"point":[-87,-27835,14701,-25210,-19,-537,-28]}
{"point":[-89,-27833,14701,-25129,-22,-366,-28]}
{"point":[-90,-27833,14699,-25047,-27,-38,-27]}
{"point":[-93,-27831,14701,-24852,-24,112,-28]}
{"point":[-92,-27829,14699,-24754,-20,266,-28]}
{"point":[-92,-27829,14701,-24656,-27,646,-27]}
{"point":[-92,-27829,14699,-24548,-26,848,-27]}
{"point":[-93,-27829,14699,-24329,-26,1060,-28]}
{"point":[-92,-27829,14701,-24216,-28,1518,-28]}
{"point":[-90,-27828,14699,-24082,-35,1737,-28]}
{"point":[-92,-27828,14701,-23849,-34,1962,-28]}
{"point":[-90,-27827,14697,-23714,-34,2167,-28]}
{"point":[-90,-27827,14699,-23564,-37,2562,-27]}
{"point":[-89,-27825,14701,-23440,-37,2819,-27]}
{"point":[-82,-27827,14702,-23163,-39,3029,-28]}
{"point":[-79,-27827,14703,-22995,-42,3507,-27]}
{"point":[-78,-27827,14702,-22814,-38,3782,-28]}
{"point":[-74,-27829,14701,-22473,-42,4022,-28]}
{"point":[-78,-27825,14701,-22313,-37,4279,-27]}
{"point":[-78,-27824,14698,-22119,-38,4754,-26]}
{"point":[-78,-27810,14698,-21960,-42,4964,-27]}
{"point":[-75,-27806,14706,-21643,-43,5222,-28]}
{"point":[-74,-27802,14705,-21426,-45,5776,-27]}
{"point":[-71,-27789,14703,-21294,-48,6053,-26]}
{"point":[-74,-27784,14703,-20890,-48,6343,-26]}
{"point":[-72,-27776,14703,-20716,-43,6762,-27]}
{"point":[-75,-27770,14703,-20512,-48,6985,-24]}
{"point":[-71,-27750,14703,-20120,-41,7245,-26]}
{"point":[-67,-27735,14699,-19894,-38,7800,-26]}
{"point":[-70,-27715,14699,-19725,-38,8040,-26]}
{"point":[-70,-27685,14699,-19533,-39,8319,-26]}
{"point":[-71,-27664,14702,-19132,-41,8576,-24]}
{"point":[-75,-27638,14702,-18877,-41,9014,-26]}
{"point":[-85,-27592,14705,-18450,-43,9251,-26]}
{"point":[-86,-27568,14705,-18262,-38,9722,-26]}
{"point":[-87,-27537,14701,-18038,-34,9935,-24]}
{"point":[-92,-27496,14697,-17793,-27,10188,-26]}
{"point":[-92,-27438,14699,-17413,-22,10443,-24]}
{"point":[-93,-27393,14699,-17222,-24,10909,-24]}
{"point":[-92,-27354,14697,-17020,-27,11137,-24]}
{"point":[-89,-27261,14701,-16589,-28,11365,-24]}
{"point":[-90,-27215,14701,-16358,-37,11579,-26]}
{"point":[-90,-27165,14702,-16089,-30,11778,-24]}
{"point":[-92,-27121,14701,-15867,-19,12157,-24]}
{"point":[-90,-27022,14697,-15379,-6,12374,-24]}
{"point":[-92,-26974,14697,-15187,-9,12814,-24]}
{"point":[-92,-26913,14695,-14967,-8,13042,-24]}
{"point":[-85,-26801,14697,-14724,-13,13281,-24]}
{"point":[-81,-26738,14703,-14290,-24,13513,-24]}
{"point":[-82,-26674,14702,-14069,-41,13881,-26]}
{"point":[-82,-26545,14701,-13521,-42,14070,-26]}
{"point":[-85,-26478,14698,-13312,-35,14239,-24]}
{"point":[-87,-26411,14694,-13119,-35,14584,-24]}
{"point":[-90,-26334,14695,-12897,-37,14762,-24]}
{"point":[-90,-26185,14695,-12415,-35,14948,-24]}
{"point":[-90,-26107,14697,-12209,-42,15290,-26]}
{"point":[-89,-26032,14698,-11931,-43,15464,-24]}
{"point":[-86,-25876,14697,-11413,-48,15630,-26]}
{"point":[-86,-25809,14692,-11199,-37,15956,-23]}
{"point":[-89,-25725,14684,-10956,-33,16114,-24]}
{"point":[-93,-25555,14676,-10487,-35,16257,-24]}
{"point":[-92,-25449,14673,-10239,-35,16567,-23]}
{"point":[-92,-25353,14675,-10045,-41,16691,-23]}
{"point":[-89,-25176,14670,-9532,-45,16803,-26]}
{"point":[-87,-25066,14669,-9288,-53,16897,-24]}
{"point":[-83,-24967,14664,-9018,-53,17159,-24]}
{"point":[-83,-24867,14654,-8791,-52,17303,-26]}
{"point":[-90,-24661,14633,-8279,-48,17420,-26]}
{"point":[-92,-24570,14624,-8054,-52,17674,-26]}
{"point":[-85,-24456,14611,-7796,-53,17811,-24]}
{"point":[-82,-24242,14607,-7575,-57,17895,-24]}
{"point":[-79,-24132,14576,-7021,-64,17981,-26]}
{"point":[-74,-24027,14559,-6758,-64,18179,-26]}
{"point":[-74,-23796,14532,-6295,-60,18275,-26]}
{"point":[-79,-23678,14522,-6067,-61,18354,-26]}
{"point":[-87,-23553,14510,-5876,-64,18513,-27]}
{"point":[-89,-23431,14499,-5619,-67,18579,-24]}
{"point":[-89,-23181,14481,-5108,-65,18643,-26]}
{"point":[-83,-23054,14475,-4877,-64,18689,-26]}
{"point":[-78,-22934,14459,-4600,-64,18804,-24]}
{"point":[-76,-22679,14433,-4080,-63,18853,-26]}
{"point":[-78,-22550,14419,-3853,-68,18893,-26]}
{"point":[-82,-22413,14400,-3628,-65,18996,-26]}
{"point":[-86,-22270,14386,-3407,-65,19037,-24]}
{"point":[-86,-22016,14348,-2922,-67,19065,-26]}
{"point":[-83,-21876,14331,-2717,-65,19149,-26]}
{"point":[-79,-21737,14316,-2434,-71,19147,-26]}
{"point":[-79,-21457,14278,-1980,-67,19189,-27]}
{"point":[-79,-21312,14268,-1783,-64,19202,-26]}
{"point":[-85,-21163,14250,-1532,-68,19213,-24]}
{"point":[-86,-20875,14224,-1102,-61,19224,-26]}
{"point":[-81,-20732,14212,-898,-65,19220,-24]}
{"point":[-81,-20584,14195,-653,-61,19223,-23]}
{"point":[-82,-20435,14183,-435,-64,19215,-24]}
{"point":[-75,-20154,14151,79,-59,19217,-26]}
{"point":[-75,-19995,14139,292,-52,19227,-23]}
{"point":[-81,-19837,14135,479,-50,19206,-24]}
{"point":[-86,-19526,14092,922,-48,19208,-24]}
{"point":[-90,-19379,14081,1093,-46,19205,-24]}
{"point":[-92,-19197,14076,1325,-48,19193,-24]}
{"point":[-82,-18896,14044,1735,-46,19194,-24]}
{"point":[-83,-18737,14028,1995,-48,19161,-24]}
{"point":[-86,-18576,14014,2182,-39,19162,-24]}
{"point":[-89,-18255,13991,2428,-31,19164,-26]}
{"point":[-92,-18090,13966,2820,-30,19169,-26]}
{"point":[-90,-17926,13962,3019,-5,19091,-24]}
{"point":[-92,-17589,13923,3378,-2,19094,-26]}
{"point":[-90,-17421,13908,3610,-4,19065,-24]}
{"point":[-90,-17256,13901,3786,-2,19047,-24]}
{"point":[-86,-17093,13885,4025,-5,19031,-26]}
{"point":[-87,-16759,13852,4379,-8,19011,-26]}
{"point":[-90,-16596,13823,4578,-5,18995,-24]}
{"point":[-89,-16428,13801,4755,-6,18999,-24]}
{"point":[-90,-16085,13775,5105,-5,18988,-24]}
{"point":[-89,-15906,13750,5259,2,18925,-24]}
{"point":[-90,-15722,13726,5442,-16,18869,-23]}
{"point":[-89,-15556,13711,5623,-17,18833,-24]}
{"point":[-81,-15222,13675,5980,-15,18823,-26]}
{"point":[-82,-15044,13661,6151,-17,18801,-26]}
{"point":[-85,-14709,13645,6337,-15,18774,-24]}
{"point":[-89,-14523,13612,6645,-13,18794,-24]}
{"point":[-86,-14345,13603,6819,-15,18808,-26]}
{"point":[-83,-14169,13579,6992,0,18686,-26]}
{"point":[-83,-13800,13537,7310,-5,18660,-24]}
{"point":[-78,-13625,13517,7472,-4,18642,-24]}
{"point":[-71,-13441,13499,7603,-6,18650,-24]}
{"point":[-81,-13094,13485,7774,-13,18634,-26]}
{"point":[-86,-12919,13448,8101,-24,18594,-26]}
{"point":[-89,-12730,13432,8234,-39,18564,-24]}
{"point":[-90,-12356,13418,8491,-38,18518,-26]}
{"point":[-90,-12170,13411,8614,-35,18499,-26]}
{"point":[-90,-11986,13402,8774,-34,18444,-26]}
{"point":[-86,-11797,13384,8929,-31,18435,-24]}
{"point":[-87,-11420,13351,9159,-34,18425,-24]}
{"point":[-92,-11232,13327,9280,-33,18407,-26]}
{"point":[-92,-11050,13310,9409,-42,18378,-26]}
{"point":[-92,-10677,13274,9650,-41,18374,-26]}
{"point":[-92,-10491,13260,9788,-42,18363,-27]}
{"point":[-93,-10316,13245,9908,-39,18354,-26]}
{"point":[-90,-10133,13233,10059,-42,18338,-27]}
{"point":[-89,-9758,13198,10307,-41,18337,-26]}
{"point":[-89,-9573,13176,10430,-43,18325,-26]}
{"point":[-89,-9386,13160,10544,-50,18323,-26]}
{"point":[-90,-8996,13126,10765,-46,18321,-26]}
{"point":[-90,-8798,13108,10855,-49,18317,-27]}
{"point":[-90,-8609,13087,10965,-42,18315,-26]}
{"point":[-90,-8210,13046,11167,-37,18329,-27]}
{"point":[-90,-8007,13025,11258,-43,18322,-26]}
{"point":[-93,-7809,13001,11354,-39,18319,-27]}
{"point":[-93,-7621,12983,11443,-42,18323,-27]}
{"point":[-94,-7242,12952,11642,-38,18327,-26]}
{"point":[-93,-7045,12936,11741,-34,18343,-24]}
{"point":[-96,-6854,12921,11841,-37,18347,-26]}
{"point":[-93,-6455,12904,12025,-38,18351,-26]}
{"point":[-92,-6247,12888,12119,-42,18338,-27]}
{"point":[-92,-6048,12874,12223,-34,18333,-26]}
{"point":[-86,-5664,12843,12418,-28,18343,-26]}
{"point":[-81,-5454,12834,12503,-33,18337,-27]}
{"point":[-82,-5251,12811,12594,-30,18340,-26]}
{"point":[-83,-5051,12789,12683,-33,18344,-24]}
{"point":[-87,-4647,12752,12854,-30,18348,-26]}
{"point":[-86,-4437,12741,12933,-33,18344,-26]}
{"point":[-87,-4033,12727,13016,-30,18344,-24]}
{"point":[-87,-3839,12701,13179,-26,18351,-26]}
{"point":[-85,-3635,12682,13241,-13,18354,-26]}
{"point":[-85,-3438,12659,13300,-9,18358,-26]}
{"point":[-87,-3029,12612,13440,-17,18358,-26]}
{"point":[-86,-2808,12594,13521,-35,18355,-26]}
{"point":[-86,-2595,12582,13605,-34,18354,-27]}
{"point":[-81,-2194,12573,13774,-33,18352,-27]}
{"point":[-67,-1994,12569,13851,-30,18354,-26]}
{"point":[-53,-1789,12556,13930,-35,18344,-27]}
{"point":[-54,-1380,12535,14105,-34,18352,-26]}
{"point":[-72,-1170,12525,14177,-35,18354,-26]}
{"point":[-85,-947,12514,14245,-49,18347,-27]}
{"point":[-85,-736,12490,14308,-50,18344,-27]}
{"point":[-92,-331,12472,14447,-52,18349,-27]}
{"point":[-93,-129,12468,14517,-48,18349,-27]}
{"point":[-92,75,12459,14587,-46,18352,-27]}
{"point":[-97,486,12437,14720,-49,18356,-27]}
{"point":[-96,692,12431,14783,-52,18356,-27]}
{"point":[-93,903,12424,14848,-56,18349,-27]}
{"point":[-93,1308,12396,14977,-53,18349,-27]}
{"point":[-93,1507,12381,15032,-48,18355,-26]}
{"point":[-94,1707,12365,15087,-52,18348,-26]}
{"point":[-96,1903,12344,15143,-52,18354,-26]}
{"point":[-96,2308,12307,15250,-50,18355,-27]}
{"point":[-90,2511,12295,15302,-50,18355,-27]}
{"point":[-92,2719,12281,15350,-54,18349,-26]}
{"point":[-81,3117,12249,15448,-52,18348,-27]}
{"point":[-76,3312,12238,15488,-46,18354,-27]}
{"point":[-72,3506,12231,15523,-50,18351,-28]}
{"point":[-72,3701,12219,15560,-43,18355,-27]}
{"point":[-71,4103,12150,15640,-46,18356,-28]}
{"point":[-64,4302,12109,15676,-52,18355,-28]}
{"point":[-74,4704,12089,15702,-54,18352,-28]}
{"point":[-76,4891,12053,15766,-54,18348,-28]}
{"point":[-74,5079,12038,15794,-53,18349,-27]}
{"point":[-74,5266,12025,15814,-48,18351,-27]}
{"point":[-93,5640,11980,15861,-46,18356,-27]}
{"point":[-97,5837,11964,15873,-53,18356,-27]}
{"point":[-97,6027,11947,15887,-52,18359,-27]}
{"point":[-96,6411,11918,15902,-57,18354,-27]}
{"point":[-94,6594,11896,15913,-50,18354,-27]}
{"point":[-93,6770,11877,15921,-50,18354,-27]}
{"point":[-94,7124,11857,15923,-54,18347,-28]}
{"point":[-96,7293,11804,15943,-52,18354,-27]}
{"point":[-96,7470,11781,15950,-50,18354,-27]}
{"point":[-94,7654,11760,15957,-50,18352,-27]}
{"point":[-87,8036,11708,15967,-50,18354,-27]}
{"point":[-87,8190,11682,15972,-56,18341,-27]}
{"point":[-86,8359,11656,15981,-52,18347,-27]}
{"point":[-85,8690,11601,15987,-52,18347,-27]}
{"point":[-89,8867,11578,15990,-53,18344,-27]}
{"point":[-94,9004,11546,16000,-46,18356,-27]}
{"point":[-97,9332,11501,16005,-49,18354,-27]}
{"point":[-97,9515,11482,16014,-52,18354,-27]}
{"point":[-94,9659,11464,16018,-53,18354,-27]}
{"point":[-83,9852,11447,16022,-52,18348,-27]}
{"point":[-81,10145,11416,16023,-48,18348,-28]}
{"point":[-82,10316,11395,16022,-52,18347,-27]}
{"point":[-85,10601,11355,16024,-49,18349,-28]}
{"point":[-86,10769,11324,16029,-45,18351,-26]}
{"point":[-83,10899,11288,16031,-48,18355,-27]}
{"point":[-85,11204,11233,16030,-49,18354,-26]}
{"point":[-83,11351,11206,16030,-52,18354,-28]}
{"point":[-71,11504,11180,16030,-53,18352,-27]}
{"point":[-38,11802,11144,16023,-54,18345,-27]}
{"point":[-34,11955,11131,16027,-49,18347,-27]}
{"point":[-37,12071,11126,16027,-48,18349,-27]}
{"point":[-43,12208,11122,16027,-43,18351,-28]}
{"point":[-60,12488,11049,16035,-43,18354,-27]}
{"point":[-67,12602,11030,16035,-49,18356,-26]}
{"point":[-78,12730,11016,16037,-49,18355,-28]}
{"point":[-74,12999,10976,16034,-50,18355,-27]}
{"point":[-60,13125,10958,16031,-52,18354,-27]}
{"point":[-41,13249,10942,16034,-52,18354,-27]}
{"point":[-33,13509,10927,16027,-54,18347,-27]}
{"point":[-37,13642,10921,16020,-53,18345,-27]}
{"point":[-42,13742,10904,16022,-52,18347,-28]}
{"point":[-46,13962,10840,16023,-50,18348,-27]}
{"point":[-43,14068,10780,16026,-46,18352,-27]}
{"point":[-43,14177,10739,16024,-52,18352,-27]}
{"point":[-50,14282,10713,16022,-50,18352,-28]}
{"point":[-43,14488,10666,16024,-50,18352,-27]}
{"point":[-34,14589,10647,16024,-52,18351,-28]}
{"point":[-28,14705,10632,16026,-49,18351,-28]}
{"point":[-30,14886,10606,16024,-50,18349,-28]}
{"point":[-37,14989,10589,16022,-49,18351,-27]}
{"point":[-39,15076,10566,16020,-54,18345,-27]}
{"point":[-39,15174,10530,16015,-54,18344,-27]}
{"point":[-54,15352,10465,16016,-53,18347,-27]}
{"point":[-64,15431,10439,16016,-52,18347,-28]}
{"point":[-71,15511,10413,16018,-52,18347,-27]}
{"point":[-81,15667,10342,16018,-49,18348,-27]}
{"point":[-81,15735,10314,16020,-49,18349,-27]}
{"point":[-85,15795,10283,16022,-50,18351,-27]}
{"point":[-90,15934,10226,16020,-52,18349,-27]}
{"point":[-94,15998,10202,16020,-52,18349,-28]}
{"point":[-97,16059,10174,16018,-53,18349,-28]}
{"point":[-98,16119,10150,16018,-54,18349,-28]}
{"point":[-98,16243,10103,16018,-54,18348,-28]}
{"point":[-98,16288,10075,16019,-56,18349,-28]}
{"point":[-98,16336,10047,16018,-54,18349,-26]}
{"point":[-96,16436,9983,16018,-54,18348,-28]}
{"point":[-96,16483,9953,16018,-53,18348,-28]}
{"point":[-94,16535,9923,16018,-52,18348,-27]}
{"point":[-87,16607,9886,16019,-53,18348,-28]}
{"point":[-83,16645,9819,16019,-52,18348,-27]}
{"point":[-78,16678,9784,16019,-49,18348,-27]}
{"point":[-76,16708,9757,16020,-50,18349,-28]}
{"point":[-75,16758,9710,16020,-48,18349,-28]}
{"point":[-75,16780,9687,16022,-49,18352,-27]}
{"point":[-76,16807,9663,16022,-48,18351,-27]}
{"point":[-71,16828,9643,16022,-49,18349,-27]}
{"point":[-67,16866,9598,16019,-48,18349,-27]}
{"point":[-61,16883,9577,16016,-48,18347,-27]}
{"point":[-60,16912,9547,16018,-46,18347,-27]}
{"point":[-56,16921,9521,16016,-43,18347,-28]}
{"point":[-48,16927,9482,16018,-33,18348,-28]}
{"point":[-39,16931,9431,16020,-37,18349,-27]}
{"point":[-39,16935,9393,16018,-37,18349,-27]}
{"point":[-37,16936,9312,16020,-38,18351,-27]}
{"point":[-34,16936,9286,16020,-45,18349,-27]}
{"point":[-28,16935,9272,16020,-42,18349,-27]}
{"point":[-26,16935,9262,16020,-41,18349,-27]}
{"point":[-22,16932,9251,16019,-37,18349,-27]}
{"point":[-20,16935,9232,16019,-19,18348,-27]}
{"point":[-15,16931,9177,16019,-13,18347,-27]}
{"point":[-15,16927,9137,16018,-16,18348,-26]}
{"point":[-12,16925,9103,16018,-22,18348,-28]}

View File

@@ -0,0 +1,26 @@
#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "rm_service.h"
#define ARM_DOF 6
class RmArmHandler
{
private:
RM_Service roboticService_;
rm_robot_handle robotHandle_;
public:
RmArmHandler(RM_Service* roboticService, rm_robot_handle* robotHandle);
~RmArmHandler();
void CustomApiLog(const char* message, va_list args);
void ExecuteMovejCanfd(float points[ARM_DOF]);
};

View File

@@ -0,0 +1,45 @@
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/float64_multi_array.hpp"
#include "sensor_msgs/msg/joint_state.hpp"
#include "rm_service.h"
#include "rm_arm_handler.hpp"
using namespace std::chrono_literals;
using std::placeholders::_1;
class RmArmNode: public rclcpp::Node
{
public:
RmArmNode();
~RmArmNode();
private:
RM_Service *rmService_;
rm_robot_handle *leftRmRobotHandler_;
rm_robot_handle *rightRmRobotHandler_;
RmArmHandler *leftArmHandler_;
RmArmHandler *rightArmHandler_;
static float left_q_in_joint[ARM_DOF];
static float right_q_in_joint[ARM_DOF];
rclcpp::Subscription<std_msgs::msg::Float64MultiArray>::SharedPtr leftArmCommandSub_;
rclcpp::Subscription<std_msgs::msg::Float64MultiArray>::SharedPtr rightArmCommandSub_;
static rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr leftArmStatesPub_;
static rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr rightArmStatesPub_;
static void CallbackRmRealtimeArmJointState(rm_realtime_arm_joint_state_t data);
static void custom_api_log(const char* message, va_list args);
void leftArmCommandCallback(const std_msgs::msg::Float64MultiArray::SharedPtr msg);
void rightArmCommandCallback(const std_msgs::msg::Float64MultiArray::SharedPtr msg);
};

View File

@@ -0,0 +1,23 @@
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import TimerAction
def generate_launch_description():
# 创建节点启动描述
rm_arm_control_node = Node(
package='rm_arm_control', # 功能包名称
executable='rm_arm_control', # 可执行文件名称
name='rm_arm_control_node', # 节点名称(可自定义)
output='screen', # 输出到屏幕
parameters=[{"use_sim_time": False}] # 启用模拟时间
)
start_rm_arm_control_node = TimerAction(
period=1.0,
actions=[rm_arm_control_node],
)
# 组装launch描述
return LaunchDescription([
start_rm_arm_control_node
])

View File

@@ -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>rm_arm_control</name>
<version>1.0.0</version>
<description>rm_arm_control package</description>
<maintainer email="Ray@email.com">Your Name</maintainer>
<license>Apache-2.0</license>
<build_depend>ament_cmake</build_depend>
<exec_depend>rclcpp</exec_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>nav_msgs</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -0,0 +1,258 @@
# RMDemo_MovejCANFD
## **1. 项目介绍**
本项目是一个使用睿尔曼C开发包,完成工程完成读取demo下的关节角度轨迹文件按照10ms的周期进行透传机械臂可以稳定运行不同型号机械臂的透传轨迹文件注册机械臂实时状态的回调函数透传过程中可以实时获取机械臂当前角度。
## **2. 代码结构**
```
RMDemo_MovejCANFD
├── build # CMake构建生成的输出目录如Makefile、构建文件等
├── cmake # CMake模块和脚本的存放目录
│ ├── ...
├── data
│ └── robot_log.txt # 日志、轨迹文件等数据文件目录(在执行过程中生成)
├── include # 自定义头文件存放目录
├── Robotic_Arm 睿尔曼机械臂二次开发包
│ ├── include
│ │ ├── rm_define.h # 机械臂的定义
│ │ └── rm_interface.h # 机械臂 API 的接口头文件
│ ├── lib
│ │ ├── api_c.dll # Windows 的 API 库
│ │ ├── api_c.lib # Windows 的 API 库
│ │ └── libapi_c.so # Linux 的 API 库
├── src
│ ├── main.c # 主函数
├── CMakeLists.txt # 项目的顶层CMake配置文件
├── readme.md # 为示例工程提供详细的文档
├── run.bat # 快速运行脚本, Windows为bat脚本
└── run.sh # 快速运行脚本, linux为shell脚本
```
## **3. 系统要求**
- 操作系统Ubuntu 18.04或更高版本
- 编译器GCC 7.5或更高版本 (或任何其他兼容的C编译器)
- 依赖库:
- CMake 3.10或更高版本
- RMAPI库(包含在 `Robotic_Arm/lib`目录中)
## **4. 安装说明**
1. 克隆项目到本地:
```bash
```
2. 构建项目:
Linux下
cmake:
```bash
mkdir build
cd build
cmake ..
make
```
如果是GC编译的话
```bash
#!/bin/bash
# 编译并链接
gcc -I./include -I./Robotic_Arm/include -L./Robotic_Arm/lib -Wl,-rpath=./Robotic_Arm/lib -DDATA_FILE_PATH=\"$(pwd)/data/RM65\&RM63_canfd_data.txt\" -o RMDemo_MovejCANFD src/main.c -lapi_c
# 检查编译是否成功
if [ $? -eq 0 ]; then
# 设置LD_LIBRARY_PATH环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./Robotic_Arm/lib
# 运行编译后的可执行文件
./RMDemo_MovejCANFD
else
echo "编译失败"
fi
```
## **5. 注意事项**
该Demo以RM65-B型号机械臂为例请根据实际情况修改代码中的数据。
## **6. 使用指南**
### **1. 快速运行**
按照以下步骤快速运行代码:
1. **配置机械臂IP地址**:打开 `main.c` 文件,在 `main` 函数中修改 `robot_ip_address` 类的初始化参数为当前机械臂的IP地址默认IP地址为 `"192.168.1.18"`。
```C
const char *robot_ip_address = "192.168.1.18";
int robot_port = 8080;
rm_robot_handle *robot_handle = rm_create_robot_arm(robot_ip_address, robot_port);
```
2. **命令行运行**:在终端进入 `RMDemo_MovejCANFD` 目录,输入以下命令运行 C程序
2.1 Linux下
* ```bash
chmod +x run.sh
./run.sh
```
2.2 Windows下: 双击运行 run.bat
2.3 **选择对应型号轨迹文件**
- 在方法中`demo_movej_canfd`中:
```C
void demo_movej_canfd(rm_robot_handle* handle) {
printf("Trying to open file: %s\n", DATA_FILE_PATH);
FILE* file = fopen(DATA_FILE_PATH, "r");
if (!file) {
perror("Failed to open file");
return;
}
float points[MAX_POINTS][ARM_DOF];
int point_count = 0;
while (fscanf(file, "%f,%f,%f,%f,%f,%f,%f",
&points[point_count][0], &points[point_count][1], &points[point_count][2],
&points[point_count][3], &points[point_count][4], &points[point_count][5],
&points[point_count][6]) == ARM_DOF) {
point_count++;
}
```
- ECO65ECO65_canfd_data.txt
- RM65&RML63-ⅡRM65&RM63_canfd_data.txt
- RM75RM75_canfd_data.txt
- DATA_FILE_PATH 为了跨平台在[CMakeLists.txt](CMakeLists.txt) 里面通过 # 将数据文件路径定义为预处理器宏
add_definitions(-DDATA_FILE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/data/RM65&RM63_canfd_data.txt")
-
### **2. 代码说明**
下面是 `main.c` 文件的主要功能:
- **连接机械臂**
```C
rm_robot_handle *robot_handle = rm_create_robot_arm(robot_ip_address, robot_port);
```
连接到指定IP和端口的机械臂。
- **获取API版本**
```C
char *api_version = rm_api_version();
printf("API Version: %s.\n", api_version);
```
获取并显示API版本。
- **配置实时推送**
```C
rm_realtime_push_config_t config = {5, true, 8089, 0, "192.168.1.88"};
int result = rm_set_realtime_push(robot_handle, config);
```
- **读取关节角度轨迹文件并进行透传**
```C
demo_movej_canfd(robot_handle)
```
- **断开机械臂连接**
```C
disconnect_robot_arm(robot_handle);
```
### **3. 运行结果示例**
运行脚本后,输出结果如下所示:
```
API Version: 0.3.0.
Robot handle created successfully: 1
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Joint positions:
0.01 -21.32 -78.51 -0.03 -80.17 0.02
Successfully set realtime push configuration.
Current state: -53.000
Current angles: 0.013 -21.323 -78.511 -0.034 -80.170 0.020
Current state: 0
Current state: 0
Error Code: 0
Arm IP: 192.168.1.18
Arm Error: 0
Joint Position:
0.013
-21.323
-78.511
-0.034
-80.170
0.020
Force Sensor:
Coordinate: 888729056
System Error: 0
Waypoint:
Euler: [3.141, 0.000, 0.000]
Position: [0.300, -0.000, 0.299]
Quat: [0.000, 1.000, -0.000, -0.000]
....
176.12 -128.64 133.60 176.12 126.63 356.12
Pass-through completed
The motion is complete, the arm is in place.
Motion result: 1
Current device: 0
Is the next trajectory connected: 0
movej_cmd joint movement 1: 0
...
Joint positions:
-0.00 0.00 0.00 0.00 0.00 -0.00
INFO: disconnect_robot_arm: Operation successful
```
#### 2运行脚本后运行轨迹从上至下如下图所示
![moveCANFD](moveCANFD.gif)
## **6. 许可证信息**
* 本项目遵循MIT许可证。
## **7. 常见问题解答FAQ**
- **Q:** 如何解决编译错误?
**A:** 请确保您的编译器版本和依赖库满足系统要求,并按照安装说明重新配置环境。
- **Q:** 如何连接机器人?
**A:** 请参考示例代码中的连接步骤确保机器人IP地址和端口正确配置。
- **Q:** UDP数据推送接口收不到数据
**A:** 检查线程模式、是否使能推送数据、IP以及防火墙
- - **Q:** Trying to open file: C:/Users/dell/Videos/710/RMDemo_MovejCANFD/data/RM65&RM63_canfd_data.txt No valid points data found in file
**A:** #define ARM_DOF 6 在 RMDemo_MovejCANFD\include\utils.h 下是否未定义

View File

@@ -0,0 +1,27 @@
#!/bin/bash
# 设置构建目录
BUILD_DIR=build
# 如果构建目录存在,则删除
if [ -d "$BUILD_DIR" ]; then
rm -rf $BUILD_DIR
fi
# 创建构建目录
mkdir $BUILD_DIR
# 进入构建目录
cd $BUILD_DIR
# 运行CMake配置
cmake ..
# 运行make进行编译
make
# 返回根目录
cd ..
# 运行编译后的可执行文件
./build/RMDemo_MovejCANFD

View File

@@ -0,0 +1,46 @@
#include "rm_arm_handler.hpp"
RmArmHandler::RmArmHandler(RM_Service* roboticService, rm_robot_handle* robotHandle)
{
roboticService_ = *roboticService;
robotHandle_ = *robotHandle;
}
RmArmHandler::~RmArmHandler()
{
}
void RmArmHandler::CustomApiLog(const char* message, va_list args) {
if (!message) {
fprintf(stderr, "Error: message is a null pointer\n");
return;
}
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), message, args);
printf(" %s\n", buffer);
}
void RmArmHandler::ExecuteMovejCanfd(float points[ARM_DOF])
{
rm_robot_info_t robot_info;
int info_result = roboticService_.rm_get_robot_info(&robotHandle_, &robot_info);
if (info_result != 0) {
printf("Failed to get robot info\n");
return;
}
int dof = robot_info.arm_dof;
if (dof != ARM_DOF) {
printf("Invalid degree of freedom\n");
return;
}
int result = roboticService_.rm_movej_canfd(&robotHandle_, points, false, 0);
if (result != 0) {
printf("Error moving to joint space! \n");
}
printf("Pass-through completed\n");
}

View File

@@ -0,0 +1,183 @@
#include "rm_arm_node.hpp"
float RmArmNode::left_q_in_joint[ARM_DOF] = {0.0f};
float RmArmNode::right_q_in_joint[ARM_DOF] = {0.0f};
rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr RmArmNode::leftArmStatesPub_ = nullptr;
rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr RmArmNode::rightArmStatesPub_ = nullptr;
// 机器人控制器构造函数
RmArmNode::RmArmNode() : Node("robot_Fsm_node")
{
rmService_ = new RM_Service();
rmService_->rm_set_log_call_back(custom_api_log, 3);
rmService_->rm_realtime_arm_state_call_back(CallbackRmRealtimeArmJointState);
const char *robot_ip_address = "192.168.1.16";
int robot_port = 8080;
leftRmRobotHandler_ = rmService_->rm_create_robot_arm(robot_ip_address, robot_port);
if (leftRmRobotHandler_ == NULL) {
throw std::runtime_error("Failed to create left arm handle.");
}
leftArmHandler_ = new RmArmHandler(rmService_,leftRmRobotHandler_);
robot_ip_address = "192.168.1.18";
robot_port = 8080;
rightRmRobotHandler_ = rmService_->rm_create_robot_arm(robot_ip_address, robot_port);
if (rightRmRobotHandler_ == NULL) {
throw std::runtime_error("Failed to create right arm handle.");
}
rightArmHandler_ = new RmArmHandler(rmService_,rightRmRobotHandler_);
leftArmStatesPub_ = this->create_publisher<sensor_msgs::msg::JointState>("left_arm_states", 10);
rightArmStatesPub_ = this->create_publisher<sensor_msgs::msg::JointState>("right_arm_states", 10);
leftArmCommandSub_ = this->create_subscription<std_msgs::msg::Float64MultiArray>("left_arm_command", 10, std::bind(&RmArmNode::leftArmCommandCallback, this, std::placeholders::_1));
rightArmCommandSub_ = this->create_subscription<std_msgs::msg::Float64MultiArray>("right_arm_command", 10, std::bind(&RmArmNode::rightArmCommandCallback, this, std::placeholders::_1));
rm_realtime_push_config_t config = {100, true, 8089, 0, "192.168.1.66"};
int result1 = rmService_-> rm_set_realtime_push(leftRmRobotHandler_, config);
int result2 = rmService_-> rm_set_realtime_push(rightRmRobotHandler_, config);
if (result1 != 0 || result2 != 0) {
throw std::runtime_error("Failed to set realtime push.");
}
}
RmArmNode::~RmArmNode()
{
rmService_ -> rm_delete_robot_arm(leftRmRobotHandler_);
rmService_ -> rm_delete_robot_arm(rightRmRobotHandler_);
delete rmService_;
delete leftArmHandler_;
delete rightArmHandler_;
}
void RmArmNode::CallbackRmRealtimeArmJointState(rm_realtime_arm_joint_state_t data)
{
printf("==================MYCALLBACK==================\n");
printf("Current state: %.3f\n", data.joint_status.joint_current[0]);
printf("Current angles: %.3f %.3f %.3f %.3f %.3f %.3f\n", data.joint_status.joint_position[0], data.joint_status.joint_position[1],
data.joint_status.joint_position[2], data.joint_status.joint_position[3], data.joint_status.joint_position[4], data.joint_status.joint_position[5]);
printf("Current state: %u\n", data.joint_status.joint_err_code[0]);
// Handle received data
printf("Error Code: %d\n", data.errCode);
printf("Arm IP: %s\n", data.arm_ip);
printf("Error: ");
for (int i = 0; i < data.err.err_len; i++) {
printf("%04x ", data.err.err[i]);
}
printf("\n");
// Handle joint state
printf("Joint Position:\n");
for (int i = 0; i < ARM_DOF; i++) {
printf(" %.3f \n", data.joint_status.joint_position[i]);
}
// Handle waypoint information
printf("Waypoint:\n");
printf(" Euler: [%.3f, %.3f, %.3f]\n", data.waypoint.euler.rx, data.waypoint.euler.ry, data.waypoint.euler.rz);
printf(" Position: [%.3f, %.3f, %.3f]\n", data.waypoint.position.x, data.waypoint.position.y, data.waypoint.position.z);
printf(" Quat: [%.3f, %.3f, %.3f, %.3f]\n", data.waypoint.quaternion.w, data.waypoint.quaternion.x, data.waypoint.quaternion.y, data.waypoint.quaternion.z);
if (data.arm_ip == "192.168.1.16") {
sensor_msgs::msg::JointState leftArmStates;
leftArmStates.position = {data.joint_status.joint_position[0], data.joint_status.joint_position[1], data.joint_status.joint_position[2], data.joint_status.joint_position[3], data.joint_status.joint_position[4], data.joint_status.joint_position[5]};
for (size_t i = 0; i < ARM_DOF; i++)
{
left_q_in_joint[i] = data.joint_status.joint_position[i];
}
leftArmStatesPub_->publish(leftArmStates);
}
else if (data.arm_ip == "192.168.1.18") {
sensor_msgs::msg::JointState rightArmStates;
rightArmStates.position = {data.joint_status.joint_position[0], data.joint_status.joint_position[1], data.joint_status.joint_position[2], data.joint_status.joint_position[3], data.joint_status.joint_position[4], data.joint_status.joint_position[5]};
for (size_t i = 0; i < ARM_DOF; i++)
{
right_q_in_joint[i] = data.joint_status.joint_position[i];
}
rightArmStatesPub_->publish(rightArmStates);
}
}
void RmArmNode::custom_api_log(const char* message, va_list args)
{
if (!message) {
fprintf(stderr, "Error: message is a null pointer\n");
return;
}
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), message, args);
printf(" %s\n", buffer);
}
void RmArmNode::leftArmCommandCallback(const std_msgs::msg::Float64MultiArray::SharedPtr msg)
{
// 创建一个float数组并复制数据
std::vector<float> positions(*msg->data.begin(), *msg->data.end());
rm_inverse_kinematics_params_t inverse_params;
float q_out[ARM_DOF] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
memcpy(inverse_params.q_in, left_q_in_joint, sizeof(left_q_in_joint));
inverse_params.q_pose.position.x = positions[0];
inverse_params.q_pose.position.y = positions[1];
inverse_params.q_pose.position.z = positions[2];
inverse_params.q_pose.euler.rx = positions[3];
inverse_params.q_pose.euler.ry = positions[4];
inverse_params.q_pose.euler.rz = positions[5];
inverse_params.flag = 1;
int result = rm_algo_inverse_kinematics(NULL, inverse_params, q_out);
if (result != 0)
{
printf("Failed to calculate inverse kinematics.");
return;
}
leftArmHandler_->ExecuteMovejCanfd(q_out);
}
void RmArmNode::rightArmCommandCallback(const std_msgs::msg::Float64MultiArray::SharedPtr msg)
{
// 创建一个float数组并复制数据
std::vector<float> positions(*msg->data.begin(), *msg->data.end());
rm_inverse_kinematics_params_t inverse_params;
float q_out[ARM_DOF] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
memcpy(inverse_params.q_in, right_q_in_joint, sizeof(right_q_in_joint));
inverse_params.q_pose.position.x = positions[0];
inverse_params.q_pose.position.y = positions[1];
inverse_params.q_pose.position.z = positions[2];
inverse_params.q_pose.euler.rx = positions[3];
inverse_params.q_pose.euler.ry = positions[4];
inverse_params.q_pose.euler.rz = positions[5];
inverse_params.flag = 1;
int result = rm_algo_inverse_kinematics(NULL, inverse_params, q_out);
if (result != 0)
{
printf("Failed to calculate inverse kinematics.");
return;
}
rightArmHandler_->ExecuteMovejCanfd(positions.data());
}
int main(int argc, char *argv[]) {
rclcpp::init(argc, argv);
auto robot_fsm_node = std::make_shared<RmArmNode>();
rclcpp::spin(robot_fsm_node);
rclcpp::shutdown();
return 0;
}

View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.10)
project(robot_bringup)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 依赖
find_package(ament_cmake REQUIRED)
# 安装启动文件和世界文件
install(DIRECTORY launch
DESTINATION share/${PROJECT_NAME}/
)
ament_package()

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,21 @@
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.substitutions import FindPackageShare
from launch.substitutions import PathJoinSubstitution
def generate_launch_description():
rm_Arm_pkg_share = FindPackageShare(package="rm_arm_control")
rm_Arm_launch_path = PathJoinSubstitution(
[rm_Arm_pkg_share, "launch", "rm_arm_control.launch.py"]
)
include_rm_Arm_control = IncludeLaunchDescription(
PythonLaunchDescriptionSource(rm_Arm_launch_path),
)
return LaunchDescription([
include_rm_Arm_control,
])

View File

@@ -0,0 +1,15 @@
<?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>robot_bringup</name>
<version>1.0.0</version>
<description>Robot control package</description>
<maintainer email="Ray@email.com">Your Name</maintainer>
<license>Apache-2.0</license>
<build_depend>ament_cmake</build_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,27 +0,0 @@
cmake_minimum_required(VERSION 3.8)
project(robot_simulation)
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(gazebo_ros REQUIRED)
find_package(gazebo_plugins REQUIRED)
find_package(urdf REQUIRED)
find_package(xacro REQUIRED)
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()

View File

@@ -1,68 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
# 设置参数
use_sim_time = LaunchConfiguration('use_sim_time', default='false')
world_file_name = 'empty.world'
world_path = os.path.join(get_package_share_directory('robot_simulation'), 'worlds', world_file_name)
# URDF文件路径
urdf_file_name = 'robot.urdf.xacro'
urdf_path = os.path.join(get_package_share_directory('robot_simulation'), 'urdf', urdf_file_name)
# 启动Gazebo服务器
gazebo_server = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
os.path.join(get_package_share_directory('gazebo_ros'), 'launch'),
'/gzserver.launch.py'
]),
launch_arguments={'world': world_path}.items()
)
# 启动Gazebo客户端
gazebo_client = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
os.path.join(get_package_share_directory('gazebo_ros'), 'launch'),
'/gzclient.launch.py'
])
)
# 启动机器人状态发布器
robot_state_publisher = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
output='screen',
parameters=[{'use_sim_time': use_sim_time, 'robot_description': open(urdf_path, 'r').read()}]
)
# 启动关节状态发布器
joint_state_publisher = Node(
package='joint_state_publisher',
executable='joint_state_publisher',
name='joint_state_publisher',
output='screen',
parameters=[{'use_sim_time': use_sim_time}]
)
# 在Gazebo中生成机器人
spawn_entity = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=['-entity', 'my_robot', '-topic', 'robot_description'],
output='screen'
)
return LaunchDescription([
gazebo_server,
gazebo_client,
robot_state_publisher,
joint_state_publisher,
spawn_entity
])

View File

@@ -1,23 +0,0 @@
<?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>robot_simulation</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ray@todo.todo">ray</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>gazebo_ros</depend>
<depend>gazebo_plugins</depend>
<depend>urdf</depend>
<depend>xacro</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -1,150 +0,0 @@
<?xml version="1.0"?>
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 定义常量 -->
<xacro:property name="M_PI" value="3.141592653589793"/>
<!-- 机器人底座 -->
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.1" radius="0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 1.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.1" radius="0.2"/>
</geometry>
</collision>
<inertial>
<mass value="10.0"/>
<inertia ixx="0.2" ixy="0.0" ixz="0.0" iyy="0.2" iyz="0.0" izz="0.2"/>
</inertial>
</link>
<!-- 轮子 -->
<xacro:macro name="wheel" params="prefix x y z">
<link name="${prefix}_wheel_link">
<visual>
<geometry>
<cylinder length="0.05" radius="0.075"/>
</geometry>
<material name="gray">
<color rgba="0.5 0.5 0.5 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.05" radius="0.075"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.002"/>
</inertial>
</link>
<joint name="${prefix}_wheel_joint" type="continuous">
<origin xyz="${x} ${y} ${z}" rpy="0 ${M_PI/2} 0"/>
<parent link="base_link"/>
<child link="${prefix}_wheel_link"/>
<axis xyz="0 1 0"/>
<dynamics damping="0.1" friction="0.01"/>
</joint>
</xacro:macro>
<!-- 创建四个轮子 -->
<xacro:wheel prefix="front_left" x="0.15" y="0.2" z="-0.05"/>
<xacro:wheel prefix="front_right" x="0.15" y="-0.2" z="-0.05"/>
<xacro:wheel prefix="rear_left" x="-0.15" y="0.2" z="-0.05"/>
<xacro:wheel prefix="rear_right" x="-0.15" y="-0.2" z="-0.05"/>
<!-- 激光雷达 -->
<link name="laser_link">
<visual>
<geometry>
<cylinder length="0.05" radius="0.05"/>
</geometry>
<material name="yellow">
<color rgba="1.0 1.0 0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.05" radius="0.05"/>
</geometry>
</collision>
<inertial>
<mass value="0.1"/>
<inertia ixx="0.0001" ixy="0.0" ixz="0.0" iyy="0.0001" iyz="0.0" izz="0.0002"/>
</inertial>
</link>
<joint name="laser_joint" type="fixed">
<origin xyz="0 0 0.15" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="laser_link"/>
</joint>
<!-- Gazebo插件 -->
<!-- 差速驱动插件 -->
<gazebo>
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<ros>
<namespace>/</namespace>
<param name="cmd_vel_topic">cmd_vel</param>
<param name="odom_topic">odom</param>
<param name="odom_frame">odom</param>
<param name="base_frame">base_link</param>
</ros>
<wheel_separation>0.4</wheel_separation>
<wheel_diameter>0.15</wheel_diameter>
<update_rate>100.0</update_rate>
<publish_tf>1</publish_tf>
<odometry_frame>odom</odometry_frame>
<odometry_topic>odom</odometry_topic>
<robot_base_frame>base_link</robot_base_frame>
</plugin>
</gazebo>
<!-- 激光雷达插件 -->
<gazebo reference="laser_link">
<sensor type="ray" name="laser_sensor">
<pose>0 0 0 0 0 0</pose>
<visualize>true</visualize>
<update_rate>20.0</update_rate>
<ray>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1</resolution>
<min_angle>-3.141592653589793</min_angle>
<max_angle>3.141592653589793</max_angle>
</horizontal>
</scan>
<range>
<min>0.1</min>
<max>30.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="gazebo_ros_laser" filename="libgazebo_ros_laser.so">
<ros>
<namespace>/</namespace>
<param name="topic">scan</param>
</ros>
<frameName>laser_link</frameName>
<topicName>scan</topicName>
<visualize>true</visualize>
<updateRateHZ>20.0</updateRateHZ>
</plugin>
</sensor>
</gazebo>
</robot>