修改远控逻辑
This commit is contained in:
parent
4e6b05eb3f
commit
098e67d798
@ -91,8 +91,7 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
try_subscribe_ctrl_topic();
|
try_subscribe_ctrl_topic();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 主动连一次,后面内部 loop 线程也会自动尝试重连
|
// MQTTClientWrapper内部已有reconnectLoop线程自动处理连接和重连,无需主动调用connect()
|
||||||
mqtt_->connect();
|
|
||||||
|
|
||||||
identity_sub_ = this->create_subscription<sweeper_interfaces::msg::VehicleIdentity>(
|
identity_sub_ = this->create_subscription<sweeper_interfaces::msg::VehicleIdentity>(
|
||||||
"/vehicle/identity", rclcpp::QoS(1).transient_local().reliable(),
|
"/vehicle/identity", rclcpp::QoS(1).transient_local().reliable(),
|
||||||
@ -119,7 +118,7 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
|
|
||||||
LOG_INFO("RemoteCtrlNode started mqtt=%s client_id=%s", uri.str().c_str(), client_id.c_str());
|
LOG_INFO("RemoteCtrlNode started mqtt=%s client_id=%s", uri.str().c_str(), client_id.c_str());
|
||||||
|
|
||||||
// ===== 看门狗 & 定时发布 =====
|
// ===== 看门狗 & 消息发布 =====
|
||||||
last_msg_time_ = std::chrono::steady_clock::now();
|
last_msg_time_ = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
// 看门狗:800ms 内没收到新 remote 指令则停控
|
// 看门狗:800ms 内没收到新 remote 指令则停控
|
||||||
@ -142,43 +141,6 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 周期发布到 /remote_mc_ctrl
|
|
||||||
timer_ = this->create_wall_timer(20ms, // 50Hz
|
|
||||||
[this]()
|
|
||||||
{
|
|
||||||
// 未授权远控:完全不发
|
|
||||||
if (!remote_authorized_.load(std::memory_order_acquire)) return;
|
|
||||||
|
|
||||||
sweeperMsg::McCtrl msg;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(state_mtx_);
|
|
||||||
|
|
||||||
// 已授权但超时:安全停车态
|
|
||||||
if (!remote_alive_.load(std::memory_order_relaxed))
|
|
||||||
{
|
|
||||||
msg.gear = desired_.gear; // 通常保持挡位
|
|
||||||
msg.brake = 1;
|
|
||||||
msg.rpm = 0;
|
|
||||||
msg.angle = 0.0f;
|
|
||||||
msg.angle_speed = 120;
|
|
||||||
msg.sweep = false;
|
|
||||||
}
|
|
||||||
// 正常远控
|
|
||||||
else
|
|
||||||
{
|
|
||||||
msg.gear = desired_.gear;
|
|
||||||
msg.brake = desired_.brake;
|
|
||||||
msg.rpm = desired_.rpm;
|
|
||||||
msg.angle = desired_.angle;
|
|
||||||
msg.angle_speed = desired_.angle_speed;
|
|
||||||
msg.sweep = desired_.sweep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub_->publish(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
// CAN 反馈订阅,用于计算当前转向角
|
// CAN 反馈订阅,用于计算当前转向角
|
||||||
can_sub_ = this->create_subscription<sweeperMsg::CanFrame>(
|
can_sub_ = this->create_subscription<sweeperMsg::CanFrame>(
|
||||||
"can_data", 10,
|
"can_data", 10,
|
||||||
@ -216,36 +178,77 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
mqtt_->subscribe(ctrl_topic_, 1);
|
mqtt_->subscribe(ctrl_topic_, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void publishMcCtrlMsg()
|
||||||
|
{
|
||||||
|
// 未授权时:完全不发
|
||||||
|
if (!remote_authorized_.load(std::memory_order_acquire))
|
||||||
|
{
|
||||||
|
LOG_WARN("[REMOTE] NOT AUTHORIZED - will NOT publish message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sweeperMsg::McCtrl msg;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(state_mtx_);
|
||||||
|
|
||||||
|
// 已授权但超时:安全停车状态
|
||||||
|
if (!remote_alive_.load(std::memory_order_relaxed))
|
||||||
|
{
|
||||||
|
LOG_WARN("[REMOTE] REMOTE ALIVE=FALSE - sending safe-stop message");
|
||||||
|
msg.gear = desired_.gear;
|
||||||
|
msg.brake = 1;
|
||||||
|
msg.rpm = 0;
|
||||||
|
msg.angle = 0.0f;
|
||||||
|
msg.angle_speed = 120;
|
||||||
|
msg.sweep = false;
|
||||||
|
}
|
||||||
|
// 正常远控
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_INFO("[REMOTE] REMOTE ALIVE=TRUE - sending normal control message");
|
||||||
|
msg.gear = desired_.gear;
|
||||||
|
msg.brake = desired_.brake;
|
||||||
|
msg.rpm = desired_.rpm;
|
||||||
|
msg.angle = desired_.angle;
|
||||||
|
msg.angle_speed = desired_.angle_speed;
|
||||||
|
msg.sweep = desired_.sweep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("[REMOTE] Publishing McCtrl: gear=%d brake=%d rpm=%d angle=%.1f angle_speed=%u sweep=%d", msg.gear,
|
||||||
|
msg.brake, msg.rpm, msg.angle, msg.angle_speed, msg.sweep);
|
||||||
|
|
||||||
|
pub_->publish(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void onMqttMessage(const std::string& payload)
|
void onMqttMessage(const std::string& payload)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(state_mtx_);
|
std::lock_guard<std::mutex> lock(state_mtx_);
|
||||||
|
|
||||||
// =====================================================
|
|
||||||
// 1. MQTT 消息到达:刷新活性 & 判断是否从超时中恢复
|
|
||||||
// =====================================================
|
|
||||||
bool was_dead = !remote_alive_.exchange(true, std::memory_order_relaxed);
|
|
||||||
if (was_dead)
|
|
||||||
{
|
|
||||||
LOG_INFO("[REMOTE] control recovered");
|
|
||||||
}
|
|
||||||
|
|
||||||
last_msg_time_ = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
LOG_DEBUG("[REMOTE] recv MQTT payload: %s", payload.c_str());
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto j = json::parse(payload);
|
auto j = json::parse(payload);
|
||||||
if (!j.contains("data")) return;
|
if (!j.contains("data"))
|
||||||
|
{
|
||||||
|
LOG_WARN("[REMOTE] Invalid JSON: missing 'data' field, payload=%s", payload.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& data = j["data"];
|
const auto& data = j["data"];
|
||||||
if (!data.contains("command") || !data.contains("value")) return;
|
if (!data.contains("command") || !data.contains("value"))
|
||||||
|
{
|
||||||
|
LOG_WARN("[REMOTE] Invalid JSON: missing 'command' or 'value' field");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string cmd = data["command"].get<std::string>();
|
const std::string cmd = data["command"].get<std::string>();
|
||||||
const auto& value = data["value"];
|
const auto& value = data["value"];
|
||||||
|
|
||||||
|
LOG_INFO("[REMOTE] Received command: %s", cmd.c_str());
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// 2. mode 指令:唯一的“授权 / 取消授权”入口
|
// mode 指令:唯一的授权入口
|
||||||
// =====================================================
|
// =====================================================
|
||||||
if (cmd == "mode")
|
if (cmd == "mode")
|
||||||
{
|
{
|
||||||
@ -253,35 +256,42 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
|
|
||||||
if (mode == 3)
|
if (mode == 3)
|
||||||
{
|
{
|
||||||
// 进入远控授权态(锁存)
|
|
||||||
remote_authorized_.store(true, std::memory_order_release);
|
remote_authorized_.store(true, std::memory_order_release);
|
||||||
remote_alive_.store(true, std::memory_order_relaxed);
|
remote_alive_.store(true, std::memory_order_relaxed);
|
||||||
|
|
||||||
LOG_INFO("[REMOTE] authorized (mode=3)");
|
|
||||||
}
|
}
|
||||||
else if (mode == 0)
|
else if (mode == 0)
|
||||||
{
|
{
|
||||||
// 退出远控授权态(锁存)
|
|
||||||
remote_authorized_.store(false, std::memory_order_release);
|
remote_authorized_.store(false, std::memory_order_release);
|
||||||
remote_alive_.store(false, std::memory_order_relaxed);
|
remote_alive_.store(false, std::memory_order_relaxed);
|
||||||
|
|
||||||
// 明确清空目标状态
|
|
||||||
desired_ = {};
|
desired_ = {};
|
||||||
|
|
||||||
LOG_INFO("[REMOTE] deauthorized (mode=0)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mode 指令处理完直接返回
|
publishMcCtrlMsg();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// 3. 非 mode 指令:只有在“已授权”状态下才处理
|
// 非 mode 指令:检查授权
|
||||||
// =====================================================
|
// =====================================================
|
||||||
if (!remote_authorized_.load(std::memory_order_acquire)) return;
|
if (!remote_authorized_.load(std::memory_order_acquire))
|
||||||
|
{
|
||||||
|
LOG_WARN("[REMOTE] Command '%s' received but NOT AUTHORIZED - will not process", cmd.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新活性
|
||||||
|
bool was_dead = !remote_alive_.exchange(true, std::memory_order_relaxed);
|
||||||
|
if (was_dead)
|
||||||
|
{
|
||||||
|
LOG_INFO("[REMOTE] ✓ control recovered");
|
||||||
|
}
|
||||||
|
|
||||||
|
last_msg_time_ = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
LOG_DEBUG("[REMOTE] Processing command: %s", cmd.c_str());
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// 4. drive:油门 / 刹车 / 转向
|
// drive 指令
|
||||||
// =====================================================
|
// =====================================================
|
||||||
if (cmd == "drive")
|
if (cmd == "drive")
|
||||||
{
|
{
|
||||||
@ -295,12 +305,14 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
LOG_WARN("[REMOTE] drive value is not string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("[REMOTE] drive: throttle=%d brake=%d steer=%d", throttle, brake, steer_raw);
|
||||||
|
|
||||||
if (brake > 0)
|
if (brake > 0)
|
||||||
{
|
{
|
||||||
// 刹车优先
|
|
||||||
desired_.brake = 1;
|
desired_.brake = 1;
|
||||||
desired_.rpm = 0;
|
desired_.rpm = 0;
|
||||||
desired_.angle = 0.0f;
|
desired_.angle = 0.0f;
|
||||||
@ -308,28 +320,25 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
desired_.brake = 0;
|
desired_.brake = 0;
|
||||||
|
|
||||||
// 油门映射:0~65535 -> 0~6000 rpm
|
|
||||||
desired_.rpm = (static_cast<uint32_t>(throttle) * 6000U) / 65535U;
|
desired_.rpm = (static_cast<uint32_t>(throttle) * 6000U) / 65535U;
|
||||||
|
|
||||||
// 转向映射:0~65535 -> -50~+50 度
|
|
||||||
float target_angle = (static_cast<float>(steer_raw) * 100.0f / 65535.0f) - 50.0f;
|
float target_angle = (static_cast<float>(steer_raw) * 100.0f / 65535.0f) - 50.0f;
|
||||||
|
|
||||||
float delta = target_angle - current_feedback_angle_;
|
float delta = target_angle - current_feedback_angle_;
|
||||||
float angle_speed_deg = std::fabs(delta) / DELTA_T;
|
float angle_speed_deg = std::fabs(delta) / DELTA_T;
|
||||||
float motor_rpm = angle_speed_deg * DEG_PER_SEC_TO_RPM * GEAR_RATIO;
|
float motor_rpm = angle_speed_deg * DEG_PER_SEC_TO_RPM * GEAR_RATIO;
|
||||||
|
|
||||||
desired_.angle_speed = std::clamp<uint16_t>(static_cast<uint16_t>(motor_rpm), 120, 1500);
|
desired_.angle_speed = std::clamp<uint16_t>(static_cast<uint16_t>(motor_rpm), 120, 1500);
|
||||||
|
|
||||||
desired_.angle = target_angle;
|
desired_.angle = target_angle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// 5. gear:挡位
|
// gear 指令
|
||||||
// =====================================================
|
// =====================================================
|
||||||
else if (cmd == "gear")
|
else if (cmd == "gear")
|
||||||
{
|
{
|
||||||
int gear = value.get<int>();
|
int gear = value.get<int>();
|
||||||
|
LOG_DEBUG("[REMOTE] gear command: %d", gear);
|
||||||
|
|
||||||
switch (gear)
|
switch (gear)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@ -347,16 +356,26 @@ class RemoteCtrlNode : public rclcpp::Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// 6. sweepCtrl:清扫开关
|
// sweepCtrl 指令
|
||||||
// =====================================================
|
// =====================================================
|
||||||
else if (cmd == "sweepCtrl")
|
else if (cmd == "sweepCtrl")
|
||||||
{
|
{
|
||||||
desired_.sweep = (value.get<int>() != 0);
|
int val = value.get<int>();
|
||||||
|
desired_.sweep = (val != 0);
|
||||||
|
LOG_DEBUG("[REMOTE] sweepCtrl: %d", val);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_WARN("[REMOTE] Unknown command: %s", cmd.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
publishMcCtrlMsg();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
LOG_WARN("[REMOTE] JSON parse error: %s", e.what());
|
LOG_ERROR("[REMOTE] Exception in onMqttMessage: %s, payload=%s", e.what(), payload.c_str());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user