Program Listing for File whill_node.cpp
↰ Return to documentation for file (src/whill_node.cpp
)
// Copyright (c) 2024 WHILL, Inc.
// Released under the MIT license
// https://opensource.org/licenses/mit-license.php
#include "whill_driver/whill_node.hpp"
#include <chrono>
using namespace std::chrono_literals;
using namespace std::placeholders;
namespace whill_driver
{
const std::string kDefaultPortName = "/dev/ttyUSB0";
constexpr uint16_t kDefaulPpublishIntervalMs = 500;
void WhillNode::Initialize()
{
// load parameters
declare_parameter("port_name", kDefaultPortName);
std::string port_name = get_parameter("port_name").as_string();
whill_ = std::make_shared<model_cr2::Whill>(port_name);
declare_parameter("publish_interval_ms", kDefaulPpublishIntervalMs);
int publish_interval_ms = get_parameter("publish_interval_ms").as_int();
auto publish_duration = std::chrono::duration<double, std::milli>(publish_interval_ms);
// publish
states_model_cr2_pub_ = this->create_publisher<whill_msgs::msg::ModelCr2State>(
"/whill/states/model_cr2", 10);
states_model_cr2_timer_ =
this->create_wall_timer(publish_duration, std::bind(&WhillNode::OnStatesModelCr2Timer, this));
// subscription
controller_joy_sub_ = this->create_subscription<sensor_msgs::msg::Joy>(
"/whill/controller/joy", 10, std::bind(&WhillNode::OnControllerJoy, this, _1));
controller_cmd_vel_sub_ = this->create_subscription<geometry_msgs::msg::Twist>(
"/whill/controller/cmd_vel", 10, std::bind(&WhillNode::OnControllerCmdVel, this, _1));
// service
set_power_srv_ = this->create_service<whill_msgs::srv::SetPower>(
"/whill/set_power_srv", std::bind(&WhillNode::OnSetPowerSrv, this, _1, _2, _3));
set_speed_profile_srv_ = this->create_service<whill_msgs::srv::SetSpeedProfile>(
"/whill/set_speed_profile_srv", std::bind(&WhillNode::OnSetSpeedProfileSrv, this, _1, _2, _3));
set_battery_voltage_out_srv_ = this->create_service<whill_msgs::srv::SetBatteryVoltageOut>(
"/whill/set_battery_voltage_out_srv",
std::bind(&WhillNode::OnSetBatteryVoltageOutSrv, this, _1, _2, _3));
// start sending WHILL State Dataset1
whill_->SendStartSendingDataCommand(
publish_interval_ms, model_cr2::kDatasetNumber1,
model_cr2::kSpeedMode0);
}
WhillNode::WhillNode()
: Node("whill")
{
this->Initialize();
}
WhillNode::WhillNode(const rclcpp::NodeOptions & options)
: Node("whill_node", options)
{
this->Initialize();
}
WhillNode::~WhillNode()
{
}
void WhillNode::OnStatesModelCr2Timer()
{
auto msg = std::make_shared<whill_msgs::msg::ModelCr2State>();
if (whill_->ReceiveDataset1(msg) < 1) {return;}
states_model_cr2_pub_->publish(*msg);
}
void WhillNode::OnControllerJoy(const sensor_msgs::msg::Joy::SharedPtr joy)
{
whill_->SendSetJoystickCommand(ConvertToWhillJoy(joy->axes[1]), ConvertToWhillJoy(-joy->axes[0]));
RCLCPP_INFO(this->get_logger(), "[Joy] front:['%f'], right:['%f']", joy->axes[1], joy->axes[0]);
}
void WhillNode::OnControllerCmdVel(const geometry_msgs::msg::Twist::SharedPtr cmd_vel)
{
// [m/s] to [km/h]: *3.6
// SetVelocityCommand takes command unit (0.004 km/h): *250
int linear = cmd_vel->linear.x * 900;
// wheel_tread: 0.496
// [rad/s] to [km/h]: *wheel_tread*3.6
// SetVelocityCommand takes command unit (0.004 km/h): *250
// The direction of rotation is reversed in ROS and SetVelocityCommand
int angular = cmd_vel->angular.z * -446.4;
whill_->SendSetVelocityCommand(linear, angular);
RCLCPP_INFO(
this->get_logger(), "[CmdVel] linear:['%f'], angular:['%f']", cmd_vel->linear.x,
cmd_vel->angular.z);
}
void WhillNode::OnSetPowerSrv(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<whill_msgs::srv::SetPower::Request> request,
const std::shared_ptr<whill_msgs::srv::SetPower::Response> response)
{
(void)request_header;
switch (request->p0) {
case 0:
whill_->SetPowerOff();
RCLCPP_INFO(this->get_logger(), "WHILL power off");
response->result = 1;
break;
case 1:
whill_->SetPowerOn();
usleep(10000);
whill_->SetPowerOn();
usleep(2000);
RCLCPP_INFO(this->get_logger(), "WHILL power on");
response->result = 1;
break;
default:
RCLCPP_WARN(this->get_logger(), "p0 must be assinged 0 or 1");
response->result = -1;
break;
}
}
void WhillNode::OnSetSpeedProfileSrv(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<whill_msgs::srv::SetSpeedProfile::Request> request,
const std::shared_ptr<whill_msgs::srv::SetSpeedProfile::Response> response)
{
(void)request_header;
response->result = -1;
uint8_t s1 = uint8_t(request->s1);
uint8_t fm1 = uint8_t(request->fm1);
uint8_t fa1 = uint8_t(request->fa1);
uint8_t fd1 = uint8_t(request->fd1);
uint8_t rm1 = uint8_t(request->rm1);
uint8_t ra1 = uint8_t(request->ra1);
uint8_t rd1 = uint8_t(request->rd1);
uint8_t tm1 = uint8_t(request->tm1);
uint8_t ta1 = uint8_t(request->ta1);
uint8_t td1 = uint8_t(request->td1);
if (IsOutside(s1, 0, 5)) {
RCLCPP_WARN(this->get_logger(), "s1 must be assingned between 0 - 5");
return;
}
if (IsOutside(fm1, 8, 60)) {
RCLCPP_WARN(this->get_logger(), "fm1 must be assingned between 8 - 60");
return;
}
if (IsOutside(fa1, 10, 90)) {
RCLCPP_WARN(this->get_logger(), "fa1 must be assingned between 10 - 90");
return;
}
if (IsOutside(fd1, 40, 160)) {
RCLCPP_WARN(this->get_logger(), "fd1 must be assingned between 40 - 160");
return;
}
if (IsOutside(rm1, 8, 30)) {
RCLCPP_WARN(this->get_logger(), "rm1 must be assingned between 8 - 30");
return;
}
if (IsOutside(ra1, 10, 50)) {
RCLCPP_WARN(this->get_logger(), "ra1 must be assingned between 10 - 50");
return;
}
if (IsOutside(rd1, 40, 90)) {
RCLCPP_WARN(this->get_logger(), "rd1 must be assingned between 40 - 90");
return;
}
if (IsOutside(tm1, 8, 35)) {
RCLCPP_WARN(this->get_logger(), "tm1 must be assingned between 8 - 35");
return;
}
if (IsOutside(ta1, 10, 60)) {
RCLCPP_WARN(this->get_logger(), "ta1 must be assingned between 10 - 60");
return;
}
if (IsOutside(td1, 40, 160)) {
RCLCPP_WARN(this->get_logger(), "td1 must be assingned between 40 - 160");
return;
}
RCLCPP_INFO(this->get_logger(), "Speed profile is set");
whill_->SendSetSpeedProfileCommand(s1, fm1, fa1, fd1, rm1, ra1, rd1, tm1, ta1, td1);
response->result = 1;
}
void WhillNode::OnSetBatteryVoltageOutSrv(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<whill_msgs::srv::SetBatteryVoltageOut::Request> request,
const std::shared_ptr<whill_msgs::srv::SetBatteryVoltageOut::Response> response)
{
(void)request_header;
switch (request->v0) {
case 0:
whill_->SendSetBatteryVoltageOutCommand(0);
RCLCPP_INFO(this->get_logger(), "battery voltage out: disable");
response->result = 1;
break;
case 1:
whill_->SendSetBatteryVoltageOutCommand(1);
RCLCPP_INFO(this->get_logger(), "battery voltage out: enable");
response->result = 1;
break;
default:
RCLCPP_WARN(this->get_logger(), "v0 must be assigned 0 or 1");
response->result = -1;
break;
}
RCLCPP_WARN(this->get_logger(), "BatteryVoltageOut command is not available on Model CR2!!");
}
int WhillNode::ConvertToWhillJoy(float raw_joy)
{
int joy = (int)(raw_joy * 100.0f);
if (joy < -100) {return -100;}
if (joy > 100) {return 100;}
return joy;
}
bool WhillNode::IsOutside(uint8_t target, uint8_t end1, uint8_t end2)
{
uint8_t bottom = (end1 < end2) ? end1 : end2;
uint8_t top = (end1 < end2) ? end2 : end1;
if (target > top) {return true;}
if (target < bottom) {return true;}
return false;
}
} // namespace whill_driver
RCLCPP_COMPONENTS_REGISTER_NODE(whill_driver::WhillNode)