|
1 | 1 | // |
2 | | -// Created by yezi on 2021/12/3. |
| 2 | +// Created by qiayuan on 2022/7/29. |
3 | 3 | // |
4 | 4 |
|
5 | | -#include <rm_chassis_controllers/omni.h> |
6 | | -#include <rm_common/ros_utilities.h> |
7 | 5 | #include <string> |
| 6 | +#include <Eigen/QR> |
| 7 | + |
| 8 | +#include <rm_common/ros_utilities.h> |
8 | 9 | #include <pluginlib/class_list_macros.hpp> |
9 | 10 |
|
| 11 | +#include "rm_chassis_controllers/omni.h" |
| 12 | + |
10 | 13 | namespace rm_chassis_controllers |
11 | 14 | { |
12 | 15 | bool OmniController::init(hardware_interface::RobotHW* robot_hw, ros::NodeHandle& root_nh, |
13 | 16 | ros::NodeHandle& controller_nh) |
14 | 17 | { |
15 | 18 | ChassisBase::init(robot_hw, root_nh, controller_nh); |
16 | | - if (!controller_nh.getParam("chassis_radius", chassis_radius_)) |
| 19 | + |
| 20 | + XmlRpc::XmlRpcValue wheels; |
| 21 | + controller_nh.getParam("wheels", wheels); |
| 22 | + chassis2joints_.resize(wheels.size(), 3); |
| 23 | + |
| 24 | + size_t i = 0; |
| 25 | + for (const auto& wheel : wheels) |
17 | 26 | { |
18 | | - ROS_ERROR("chassis_radius is not set"); |
19 | | - return false; |
| 27 | + ROS_ASSERT(wheel.second.hasMember("pose")); |
| 28 | + ROS_ASSERT(wheel.second["pose"].getType() == XmlRpc::XmlRpcValue::TypeArray); |
| 29 | + ROS_ASSERT(wheel.second["pose"].size() == 3); |
| 30 | + ROS_ASSERT(wheel.second.hasMember("roller_angle")); |
| 31 | + ROS_ASSERT(wheel.second.hasMember("radius")); |
| 32 | + |
| 33 | + // Ref: Modern Robotics, Chapter 13.2: Omnidirectional Wheeled Mobile Robots |
| 34 | + Eigen::MatrixXd direction(1, 2), in_wheel(2, 2), in_chassis(2, 3); |
| 35 | + double beta = (double)wheel.second["pose"][2]; |
| 36 | + double roller_angle = (double)wheel.second["roller_angle"]; |
| 37 | + direction << 1, tan(roller_angle); |
| 38 | + in_wheel << cos(beta), sin(beta), -sin(beta), cos(beta); |
| 39 | + in_chassis << -(double)wheel.second["pose"][1], 1., 0., (double)wheel.second["pose"][0], 0., 1.; |
| 40 | + Eigen::MatrixXd chassis2joint = 1. / (double)wheel.second["radius"] * direction * in_wheel * in_chassis; |
| 41 | + chassis2joints_.block<1, 3>(i, 0) = chassis2joint; |
| 42 | + |
| 43 | + ros::NodeHandle nh_wheel = ros::NodeHandle(controller_nh, "wheels/" + wheel.first); |
| 44 | + joints_.push_back(std::make_shared<effort_controllers::JointVelocityController>()); |
| 45 | + if (!joints_.back()->init(effort_joint_interface_, nh_wheel)) |
| 46 | + return false; |
| 47 | + |
| 48 | + i++; |
20 | 49 | } |
21 | | - ros::NodeHandle nh_lf = ros::NodeHandle(controller_nh, "left_front"); |
22 | | - ros::NodeHandle nh_rf = ros::NodeHandle(controller_nh, "right_front"); |
23 | | - ros::NodeHandle nh_lb = ros::NodeHandle(controller_nh, "left_back"); |
24 | | - ros::NodeHandle nh_rb = ros::NodeHandle(controller_nh, "right_back"); |
25 | | - if (!ctrl_lf_.init(effort_joint_interface_, nh_lf) || !ctrl_rf_.init(effort_joint_interface_, nh_rf) || |
26 | | - !ctrl_lb_.init(effort_joint_interface_, nh_lb) || !ctrl_rb_.init(effort_joint_interface_, nh_rb)) |
27 | | - return false; |
28 | | - joint_handles_.push_back(ctrl_lf_.joint_); |
29 | | - joint_handles_.push_back(ctrl_rf_.joint_); |
30 | | - joint_handles_.push_back(ctrl_lb_.joint_); |
31 | | - joint_handles_.push_back(ctrl_rb_.joint_); |
32 | 50 | return true; |
33 | 51 | } |
34 | 52 |
|
35 | 53 | void OmniController::moveJoint(const ros::Time& time, const ros::Duration& period) |
36 | 54 | { |
37 | | - ctrl_rf_.setCommand(((vel_cmd_.x + vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
38 | | - ctrl_lf_.setCommand(((-vel_cmd_.x + vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
39 | | - ctrl_lb_.setCommand(((-vel_cmd_.x - vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
40 | | - ctrl_rb_.setCommand(((vel_cmd_.x - vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
41 | | - ctrl_lf_.update(time, period); |
42 | | - ctrl_rf_.update(time, period); |
43 | | - ctrl_lb_.update(time, period); |
44 | | - ctrl_rb_.update(time, period); |
| 55 | + Eigen::Vector3d vel_chassis; |
| 56 | + vel_chassis << vel_cmd_.z, vel_cmd_.x, vel_cmd_.y; |
| 57 | + Eigen::VectorXd vel_joints = chassis2joints_ * vel_chassis; |
| 58 | + for (size_t i = 0; i < joints_.size(); i++) |
| 59 | + { |
| 60 | + joints_[i]->setCommand(vel_joints(i)); |
| 61 | + joints_[i]->update(time, period); |
| 62 | + } |
45 | 63 | } |
46 | 64 |
|
47 | | -geometry_msgs::Twist OmniController::forwardKinematics() |
| 65 | +geometry_msgs::Twist OmniController::odometry() |
48 | 66 | { |
49 | | - geometry_msgs::Twist vel_data; |
50 | | - double k = wheel_radius_ / 2; |
51 | | - double lf_velocity = ctrl_lf_.joint_.getVelocity(); |
52 | | - double rf_velocity = ctrl_rf_.joint_.getVelocity(); |
53 | | - double lb_velocity = ctrl_lb_.joint_.getVelocity(); |
54 | | - double rb_velocity = ctrl_rb_.joint_.getVelocity(); |
55 | | - vel_data.linear.x = k * (-lf_velocity + rf_velocity - lb_velocity + rb_velocity) / sqrt(2); |
56 | | - vel_data.linear.y = k * (lf_velocity + rf_velocity - lb_velocity - rb_velocity) / sqrt(2); |
57 | | - vel_data.angular.z = k * (lf_velocity + rf_velocity + lb_velocity + rb_velocity) / (2 * chassis_radius_); |
58 | | - return vel_data; |
| 67 | + Eigen::VectorXd vel_joints(joints_.size()); |
| 68 | + for (size_t i = 0; i < joints_.size(); i++) |
| 69 | + vel_joints[i] = joints_[i]->joint_.getVelocity(); |
| 70 | + Eigen::Vector3d vel_chassis = |
| 71 | + (chassis2joints_.transpose() * chassis2joints_).inverse() * chassis2joints_.transpose() * vel_joints; |
| 72 | + geometry_msgs::Twist twist; |
| 73 | + twist.angular.z = vel_chassis(0); |
| 74 | + twist.linear.x = vel_chassis(1); |
| 75 | + twist.linear.y = vel_chassis(2); |
| 76 | + return twist; |
59 | 77 | } |
| 78 | + |
60 | 79 | } // namespace rm_chassis_controllers |
61 | 80 | PLUGINLIB_EXPORT_CLASS(rm_chassis_controllers::OmniController, controller_interface::ControllerBase) |
0 commit comments