sweeper_e800/src/mc/src/mc.cpp

349 lines
9.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <cstdio>
#include <iostream>
#include <iomanip>
#include "mc/can_driver.h"
#include "mc/can_struct.h"
#include "mc/get_config.h"
#include "rclcpp/rclcpp.hpp"
#include "mc/msg/can_frame.hpp"
#include "mc/msg/mc_ctrl.hpp"
CANDriver canctl;
struct CanHandlerContext
{
rclcpp::Node::SharedPtr node;
std::shared_ptr<rclcpp::Publisher<mc::msg::CanFrame>> publisher;
};
std::atomic<bool> estop_active = false; // 急停状态量
std::atomic<bool> estop_msg_received = false; // 是否收到过急停帧
std::atomic<bool> vcu_awake = false; // 是否唤醒成功
rclcpp::Time last_estop_msg_time; // 上次收到急停帧的时间
struct ControlCache
{
std::mutex mutex; // 防止多线程冲突
mc::msg::McCtrl latest_msg;
std::chrono::steady_clock::time_point last_update_time;
bool has_data = false;
void update(const mc::msg::McCtrl &msg)
{
std::lock_guard<std::mutex> lock(mutex);
latest_msg = msg;
last_update_time = std::chrono::steady_clock::now();
has_data = true;
}
bool get(mc::msg::McCtrl &msg)
{
std::lock_guard<std::mutex> lock(mutex);
if (!has_data)
return false;
auto now = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update_time);
if (duration.count() > 100) // ms超时
{
return false; // 数据过期
}
msg = latest_msg;
return true;
}
};
ControlCache control_cache;
// 发布检测
mc::msg::McCtrl get_safe_control()
{
mc::msg::McCtrl msg;
if (!control_cache.get(msg))
{
// 超时或未接收到控制数据,进入安全状态
msg.mcu_enabled = false;
msg.brake = 1;
msg.gear = 0;
msg.rpm = 0;
msg.brake_time_ms = 500;
msg.angle = 0;
msg.angle_speed = 120;
msg.main_brush_lift = false;
msg.edge_brush_lift = false;
msg.vacuum = false;
msg.spray = false;
msg.mud_flap = false;
msg.dust_shake = false;
msg.main_brush_spin = false;
msg.edge_brush_spin = false;
}
return msg;
}
// CAN消息接收回调
void receiveHandler(const CANFrame &frame, void *userData)
{
// 获取 ROS 2 Publisher
auto context = static_cast<CanHandlerContext *>(userData);
auto publisher = context->publisher;
auto now = context->node->now();
// 创建一个新的消息
auto msg = mc::msg::CanFrame();
msg.id = frame.id;
msg.dlc = frame.dlc;
msg.data.assign(frame.data, frame.data + frame.dlc);
// 发布消息
publisher->publish(msg);
// 终端打印
// std::cout << "CAN ID: ";
// if (frame.id > 0x7ff)
// {
// std::cout << std::hex << std::uppercase << frame.id;
// }
// else
// {
// std::cout << std::hex << std::uppercase << " " << frame.id;
// }
// std::cout << " Data: ";
// for (int i = 0; i < frame.dlc; ++i)
// {
// std::cout << std::setw(2) << std::setfill('0') << std::hex << (int)frame.data[i] << " ";
// }
// std::cout << std::dec << "\n";
// 检查是否为急停报文
if (frame.id == 0x18FA0121 && frame.dlc >= 3)
{
last_estop_msg_time = now; // 更新时间
estop_msg_received.store(true);
vcu_awake.store(true); // 唤醒成功
uint8_t estop_status = (frame.data[2] & 0x0c) >> 2; // 急停状态位
// 急停被按下
if (estop_status != 0x03)
{
if (!estop_active.load())
{
std::cout << "!!! E-STOP TRIGGERED, entering safe mode" << std::endl;
}
estop_active.store(true);
// 立即更新控制缓存,强制进入安全状态
mc::msg::McCtrl safe_msg;
safe_msg.mcu_enabled = false;
safe_msg.brake = 1;
safe_msg.gear = 0;
safe_msg.rpm = 0;
safe_msg.brake_time_ms = 500;
safe_msg.angle = 0;
safe_msg.angle_speed = 120;
safe_msg.main_brush_lift = false;
safe_msg.edge_brush_lift = false;
safe_msg.vacuum = false;
safe_msg.spray = false;
safe_msg.mud_flap = false;
safe_msg.dust_shake = false;
safe_msg.main_brush_spin = false;
safe_msg.edge_brush_spin = false;
control_cache.update(safe_msg); // 用这个覆盖已有控制指令
}
else
{
if (estop_active.load())
{
std::cout << "E-STOP released, resuming control" << std::endl;
}
estop_active.store(false);
}
}
}
void mcCtrlCallback(const mc::msg::McCtrl::SharedPtr msg)
{
if (estop_active)
return;
control_cache.update(*msg);
}
void VCUWakeUp()
{
CANFrame frame;
// 公共设置
frame.id = 0x18FF8017;
frame.dlc = 8;
frame.ext = true; // 使用扩展帧
frame.rtr = false; // 不是远程帧
// 第1帧握手帧
frame.data[0] = 0x55;
frame.data[1] = 0x21;
for (int i = 2; i < 8; ++i)
frame.data[i] = 0xFF;
canctl.sendFrame(frame);
// 延时,例如 50ms
std::this_thread::sleep_for(std::chrono::milliseconds(50));
// 第2帧启动帧
frame.data[0] = 0xAA;
frame.data[1] = 0x21;
for (int i = 2; i < 8; ++i)
frame.data[i] = 0xFF;
canctl.sendFrame(frame);
}
void setupTimers(rclcpp::Node::SharedPtr node)
{
// VCU 唤醒帧发送定时器1Hz
static rclcpp::TimerBase::SharedPtr timer_wakeup = node->create_wall_timer(
std::chrono::seconds(1), []()
{
if (!vcu_awake.load())
{
std::cout << "[VCU] 未唤醒,发送唤醒帧..." << std::endl;
VCUWakeUp(); // 发唤醒帧函数
} });
// 急停报文 watchdog 检查200ms
static rclcpp::TimerBase::SharedPtr timer_estop_watchdog = node->create_wall_timer(
std::chrono::milliseconds(200), [node]()
{
auto now = node->now();
auto elapsed = now - last_estop_msg_time;
if (estop_msg_received.load() && elapsed > rclcpp::Duration::from_seconds(0.5))
{
std::cout << "[VCU] 急停报文超时,重置唤醒状态。" << std::endl;
estop_msg_received.store(false);
vcu_awake.store(false);
} });
// MCU、VCU1 控制50Hz
static rclcpp::TimerBase::SharedPtr timer_mcu = node->create_wall_timer(
std::chrono::milliseconds(20), []()
{
mc::msg::McCtrl msg = get_safe_control();
mcu_cmd.setEnabled(msg.mcu_enabled);
mcu_cmd.setGear(msg.gear);
mcu_cmd.setRPM(msg.rpm);
mcu_cmd.setBrakeTime(msg.brake_time_ms);
mcu_cmd.setBrake(msg.brake);
mcu_cmd.pack();
canctl.sendFrame(mcu_cmd.toFrame()); });
// EPS 控制20Hz
static rclcpp::TimerBase::SharedPtr timer_eps = node->create_wall_timer(
std::chrono::milliseconds(50), []()
{
mc::msg::McCtrl msg = get_safe_control();
eps_cmd.setCenterCmd(0);
eps_cmd.setAngle(static_cast<int16_t>(msg.angle * 5));
eps_cmd.setAngularSpeed(msg.angle_speed);
eps_cmd.setControlMode(0x20);
eps_cmd.pack();
canctl.sendFrame(eps_cmd.toFrame()); });
// VCU2 控制10Hz
static rclcpp::TimerBase::SharedPtr timer_vcu = node->create_wall_timer(
std::chrono::milliseconds(100), []()
{
mc::msg::McCtrl msg = get_safe_control();
vcu1_cmd.setLeftLight(msg.left_light);
vcu1_cmd.setNightLightPercent(msg.night_light);
vcu2_cmd.setBrakeLight(msg.brake_light);
vcu2_cmd.setDustShake(msg.dust_shake);
vcu2_cmd.setEdgeBrushLift(msg.edge_brush_lift);
vcu2_cmd.setHeadlight(msg.headlight);
vcu2_cmd.setMainBrushLift(msg.main_brush_lift);
vcu2_cmd.setMudFlap(msg.mud_flap);
vcu2_cmd.setRightLight(msg.right_light);
vcu2_cmd.setSpray(msg.spray);
vcu2_cmd.setVacuum(msg.vacuum);
canctl.sendFrame(vcu1_cmd.toFrame());
canctl.sendFrame(vcu2_cmd.toFrame()); });
// 刷子控制3.3Hz
static rclcpp::TimerBase::SharedPtr timer_brush = node->create_wall_timer(
std::chrono::milliseconds(300), []()
{
mc::msg::McCtrl msg = get_safe_control();
main_brush_cmd.setBrushEnable(msg.main_brush_spin);
main_brush_cmd.setBrushPwm(msg.main_brush_pwm);
edge_brush_cmd.setBrushEnable(msg.edge_brush_spin);
edge_brush_cmd.setBrushPwm(msg.edge_brush_pwm);
canctl.sendFrame(main_brush_cmd.toFrame());
canctl.sendFrame(edge_brush_cmd.toFrame()); });
}
int main(int argc, char **argv)
{
rclcpp::init(argc, argv); // 初始化 ROS 2
printf("Hello world mc package\n");
last_estop_msg_time = rclcpp::Clock().now();
// 创建一个 ROS 2 节点
auto node = rclcpp::Node::make_shared("can_driver_node");
// 创建一个 Publisher
auto can_publisher = node->create_publisher<mc::msg::CanFrame>("can_data", 10);
// 创建 Subscriber收控制指令
auto subscriber = node->create_subscription<mc::msg::McCtrl>(
"mc_ctrl", // 订阅的 topic 名字
10, // 队列长度
mcCtrlCallback // 收到消息后的回调
);
Config mc_config;
load_config(mc_config);
if (!canctl.open(mc_config.can_dev))
{
std::cerr << "Failed to open CAN interface :";
std::cerr << mc_config.can_dev << std::endl;
return -1;
}
// 创建上下文
auto context = std::make_shared<CanHandlerContext>();
context->node = node;
context->publisher = can_publisher;
// 设置接收回调并传递上下文
canctl.setReceiveCallback(receiveHandler, (void *)context.get());
// 添加定时器设置
setupTimers(node);
// ROS 2 spin
rclcpp::spin(node);
// 关闭 CAN 接口
canctl.close();
rclcpp::shutdown(); // 关闭 ROS 2
return 0;
}