diff --git a/cmvr-es/common/CMakeLists.txt b/cmvr-es/common/CMakeLists.txt
index 3bdc2226..a60df3c2 100644
--- a/cmvr-es/common/CMakeLists.txt
+++ b/cmvr-es/common/CMakeLists.txt
@@ -4,6 +4,7 @@
file(GLOB SRC
${CMAKE_CURRENT_SOURCE_DIR}/utils/config_helper/src/config_setting.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils/ffmpeg/src/CameraCapture.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/utils/ffmpeg/src/RealSenseCapture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils/ffmpeg/src/VideoFrameEncoder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils/ffmpeg/src/VideoWriter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/curve/src/s_curve.cpp
@@ -26,6 +27,10 @@ target_link_libraries(common PUBLIC
avutil
avcodec
swscale
+ realsense2
+ opencv_core
+ opencv_imgproc
+ opencv_videoio
)
add_library(cmvr_es::common ALIAS common)
diff --git a/cmvr-es/common/config/cabin_robot.xml b/cmvr-es/common/config/cabin_robot.xml
index fa833a13..ecc9ae62 100644
--- a/cmvr-es/common/config/cabin_robot.xml
+++ b/cmvr-es/common/config/cabin_robot.xml
@@ -32,44 +32,44 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/cmvr-es/common/utils/config_helper/src/config_setting.cpp b/cmvr-es/common/utils/config_helper/src/config_setting.cpp
index a0690df6..2eaf4c98 100644
--- a/cmvr-es/common/utils/config_helper/src/config_setting.cpp
+++ b/cmvr-es/common/utils/config_helper/src/config_setting.cpp
@@ -2,18 +2,18 @@
#include
-// static std::string basePath()
-// {
-// char* cwd = ::getcwd(nullptr, 0);
-// if (!cwd) return {};
-// std::string s(cwd);
-// std::free(cwd);
-// return s + "/config/";
-// }
static std::string basePath()
{
- return "/home/lgv/cmvr/cmvr-es/output/bin/config/";
+ char* cwd = ::getcwd(nullptr, 0);
+ if (!cwd) return {};
+ std::string s(cwd);
+ std::free(cwd);
+ return s + "/config/";
}
+// static std::string basePath()
+// {
+// return "/home/lgv/cmvr/cmvr-es/output/bin/config/";
+// }
DEFINE_string(pinocchio_qp_ik_solver_config_file,
basePath() + "ik_solver_config/pinocchio_qp_ik_solver_config.pb.txt",
diff --git a/cmvr-es/common/utils/ffmpeg/include/CameraCapture.h b/cmvr-es/common/utils/ffmpeg/include/CameraCapture.h
index b3561c27..6f902185 100644
--- a/cmvr-es/common/utils/ffmpeg/include/CameraCapture.h
+++ b/cmvr-es/common/utils/ffmpeg/include/CameraCapture.h
@@ -4,26 +4,29 @@
#include
#include
#include
+#include
extern "C"{
#include
#include
#include
+#include
}
namespace ffmpeg {
struct CameraConfig {
- std::string device_name = "0"; // Windows: "0" (dshow), Linux: "0" (对应 /dev/video0)
+ std::string device_name = "0"; // Windows: "0" (dshow), Linux: "/dev/video0"
int width = 1280;
int height = 720;
int fps = 30;
- AVPixelFormat pixel_format = AV_PIX_FMT_YUV420P;
+ AVPixelFormat pixel_format = AV_PIX_FMT_YUV420P; // 保持原有的像素格式
};
class CameraCapture {
public:
- using FrameCallback = std::function;
+ // 回调函数:同时返回AVFrame和cv::Mat
+ using FrameCallback = std::function;
CameraCapture();
~CameraCapture();
@@ -54,6 +57,9 @@ namespace ffmpeg {
bool is_capturing_ = false;
AVPacket* packet_ = nullptr;
AVFrame* frame_ = nullptr;
+ AVFrame* rgb_frame_ = nullptr; // 用于转换的RGB帧
+ SwsContext* sws_ctx_ = nullptr; // 格式转换上下文
+ int64_t frame_count_ = 0;
// 初始化设备(平台相关)
int init_device();
@@ -61,6 +67,12 @@ namespace ffmpeg {
// 初始化解码器
int init_decoder();
+ // 初始化SWS上下文
+ int init_sws_context();
+
+ // 将AVFrame转换为cv::Mat
+ cv::Mat avframe_to_cvmat(AVFrame* frame);
+
// 释放资源
void cleanup();
};
diff --git a/cmvr-es/common/utils/ffmpeg/include/RealSenseCapture.h b/cmvr-es/common/utils/ffmpeg/include/RealSenseCapture.h
new file mode 100644
index 00000000..66e5578d
--- /dev/null
+++ b/cmvr-es/common/utils/ffmpeg/include/RealSenseCapture.h
@@ -0,0 +1,94 @@
+// RealSenseCapture.h
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+extern "C" {
+#include
+#include
+#include
+}
+
+namespace ffmpeg {
+
+struct RealSenseConfig {
+ std::string serial_number = ""; // 设备序列号,为空则使用第一个设备
+ int width = 1280;
+ int height = 720;
+ int fps = 30;
+ AVPixelFormat pixel_format = AV_PIX_FMT_BGR24; // RealSense输出BGR格式
+};
+
+class RealSenseCapture {
+public:
+ // 回调函数:同时返回AVFrame和cv::Mat
+ using FrameCallback = std::function;
+
+ RealSenseCapture();
+ ~RealSenseCapture();
+
+ // 初始化摄像头
+ int initialize(const RealSenseConfig& config);
+
+ // 开始捕获
+ int start_capture(FrameCallback callback);
+
+ // 停止捕获
+ void stop_capture();
+
+ // 获取当前配置
+ RealSenseConfig get_config() const { return config_; }
+
+ // 获取设备信息
+ std::string get_device_info() const;
+
+ // 获取内参
+ rs2_intrinsics get_intrinsics() const { return intrinsics_; }
+
+private:
+ RealSenseConfig config_;
+
+ // RealSense相关
+ rs2::context ctx_;
+ rs2::pipeline pipe_;
+ rs2::config rs_cfg_;
+ rs2::pipeline_profile profile_;
+ rs2_intrinsics intrinsics_;
+
+ // FFmpeg相关
+ AVFrame* frame_ = nullptr; // 用于编码的AVFrame
+ AVFrame* rgb_frame_ = nullptr; // 用于OpenCV的AVFrame
+
+ // 状态控制
+ std::atomic is_capturing_{false};
+ std::unique_ptr capture_thread_;
+ mutable std::mutex mutex_;
+
+ // 帧计数
+ int64_t frame_count_ = 0;
+
+ // 初始化设备
+ int init_device();
+
+ // 创建AVFrame
+ AVFrame* create_avframe(int width, int height, AVPixelFormat pix_fmt);
+
+ // 捕获线程函数
+ void capture_thread_func(FrameCallback callback);
+
+ // 将RealSense帧转换为AVFrame
+ AVFrame* realsense_to_avframe(rs2::frame frame);
+
+ // 将RealSense帧转换为cv::Mat
+ cv::Mat realsense_to_cvmat(rs2::frame frame);
+};
+
+} // namespace ffmpeg
\ No newline at end of file
diff --git a/cmvr-es/common/utils/ffmpeg/src/CameraCapture.cpp b/cmvr-es/common/utils/ffmpeg/src/CameraCapture.cpp
index 75ac0e70..001e0c18 100644
--- a/cmvr-es/common/utils/ffmpeg/src/CameraCapture.cpp
+++ b/cmvr-es/common/utils/ffmpeg/src/CameraCapture.cpp
@@ -9,6 +9,7 @@ namespace ffmpeg {
CameraCapture::CameraCapture() {
packet_ = av_packet_alloc();
frame_ = av_frame_alloc();
+ rgb_frame_ = av_frame_alloc();
}
CameraCapture::~CameraCapture() {
@@ -17,6 +18,8 @@ CameraCapture::~CameraCapture() {
if (packet_) av_packet_free(&packet_);
if (frame_) av_frame_free(&frame_);
+ if (rgb_frame_) av_frame_free(&rgb_frame_);
+ if (sws_ctx_) sws_freeContext(sws_ctx_);
}
int CameraCapture::initialize(const CameraConfig& config) {
@@ -36,6 +39,25 @@ int CameraCapture::initialize(const CameraConfig& config) {
return ret;
}
+ // 初始化SWS上下文
+ ret = init_sws_context();
+ if (ret < 0) {
+ std::cerr << "初始化SWS上下文失败" << std::endl;
+ return ret;
+ }
+
+ // 分配RGB帧缓冲区(用于转换)
+ rgb_frame_->width = config_.width;
+ rgb_frame_->height = config_.height;
+ rgb_frame_->format = AV_PIX_FMT_BGR24; // OpenCV使用BGR格式
+ ret = av_frame_get_buffer(rgb_frame_, 0);
+ if (ret < 0) {
+ std::cerr << "分配RGB帧缓冲区失败" << std::endl;
+ return ret;
+ }
+
+ std::cout << "摄像头初始化成功: " << config_.width << "x" << config_.height
+ << "@" << config_.fps << "fps" << std::endl;
return 0;
}
@@ -56,8 +78,15 @@ int CameraCapture::init_device() {
return -1;
}
+ // 设置摄像头参数
+ AVDictionary* options = nullptr;
+ av_dict_set(&options, "framerate", std::to_string(config_.fps).c_str(), 0);
+ av_dict_set(&options, "video_size", (std::to_string(config_.width) + "x" + std::to_string(config_.height)).c_str(), 0);
+
// 打开摄像头
- int ret = avformat_open_input(&fmt_ctx_, device_path.c_str(), input_fmt, nullptr);
+ int ret = avformat_open_input(&fmt_ctx_, device_path.c_str(), input_fmt, &options);
+ av_dict_free(&options);
+
if (ret < 0) {
char err_buf[1024];
av_strerror(ret, err_buf, sizeof(err_buf));
@@ -114,6 +143,46 @@ int CameraCapture::init_decoder() {
return 0;
}
+int CameraCapture::init_sws_context() {
+ // 获取解码器输出的像素格式
+ AVPixelFormat src_format = decoder_ctx_->pix_fmt;
+
+ sws_ctx_ = sws_getContext(
+ decoder_ctx_->width, decoder_ctx_->height, src_format,
+ config_.width, config_.height, AV_PIX_FMT_BGR24, // 转换为BGR24给OpenCV
+ SWS_BILINEAR, nullptr, nullptr, nullptr
+ );
+
+ if (!sws_ctx_) {
+ std::cerr << "创建SWS上下文失败" << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+cv::Mat CameraCapture::avframe_to_cvmat(AVFrame* frame) {
+ // 确保RGB帧可写
+ av_frame_make_writable(rgb_frame_);
+
+ // 转换到BGR24
+ sws_scale(sws_ctx_,
+ frame->data, frame->linesize, 0, frame->height,
+ rgb_frame_->data, rgb_frame_->linesize);
+
+ // 创建cv::Mat(BGR格式)
+ cv::Mat image(config_.height, config_.width, CV_8UC3);
+
+ // 复制数据(由于linesize可能不等于width*3,需要逐行复制)
+ for (int i = 0; i < config_.height; i++) {
+ memcpy(image.data + i * image.step,
+ rgb_frame_->data[0] + i * rgb_frame_->linesize[0],
+ config_.width * 3);
+ }
+
+ return image;
+}
+
int CameraCapture::start_capture(FrameCallback callback) {
if (!callback || !fmt_ctx_ || !decoder_ctx_) {
std::cerr << "参数无效或未初始化" << std::endl;
@@ -121,6 +190,9 @@ int CameraCapture::start_capture(FrameCallback callback) {
}
is_capturing_ = true;
+ frame_count_ = 0;
+
+ std::cout << "开始采集..." << std::endl;
while (is_capturing_) {
// 读取数据包
@@ -154,15 +226,18 @@ int CameraCapture::start_capture(FrameCallback callback) {
break;
}
- // 回调处理帧
- callback(frame_);
+ // 设置PTS
+ frame_->pts = frame_count_;
+
+ // 转换为cv::Mat(用于显示或OpenCV处理)
+ cv::Mat rgb_image = avframe_to_cvmat(frame_);
+
+ // 回调:同时返回AVFrame和cv::Mat
+ callback(frame_, rgb_image, frame_count_++);
}
}
av_packet_unref(packet_);
-
- // 控制帧率
- std::this_thread::sleep_for(std::chrono::milliseconds(1000 / config_.fps));
}
return 0;
diff --git a/cmvr-es/common/utils/ffmpeg/src/RealSenseCapture.cpp b/cmvr-es/common/utils/ffmpeg/src/RealSenseCapture.cpp
new file mode 100644
index 00000000..d1165a49
--- /dev/null
+++ b/cmvr-es/common/utils/ffmpeg/src/RealSenseCapture.cpp
@@ -0,0 +1,301 @@
+// RealSenseCapture.cpp
+#include "../include/RealSenseCapture.h"
+#include
+#include
+
+namespace ffmpeg {
+
+RealSenseCapture::RealSenseCapture() {
+ frame_ = av_frame_alloc();
+ rgb_frame_ = av_frame_alloc();
+}
+
+RealSenseCapture::~RealSenseCapture() {
+ stop_capture();
+
+ if (frame_) av_frame_free(&frame_);
+ if (rgb_frame_) av_frame_free(&rgb_frame_);
+}
+
+int RealSenseCapture::initialize(const RealSenseConfig& config) {
+ std::lock_guard lock(mutex_);
+
+ config_ = config;
+
+ try {
+ // 初始化设备
+ int ret = init_device();
+ if (ret < 0) {
+ std::cerr << "初始化RealSense设备失败" << std::endl;
+ return ret;
+ }
+
+ // 创建AVFrame
+ if (frame_) av_frame_free(&frame_);
+ if (rgb_frame_) av_frame_free(&rgb_frame_);
+
+ frame_ = create_avframe(config_.width, config_.height, config_.pixel_format);
+ rgb_frame_ = create_avframe(config_.width, config_.height, AV_PIX_FMT_BGR24);
+
+ if (!frame_ || !rgb_frame_) {
+ std::cerr << "创建AVFrame失败" << std::endl;
+ return -1;
+ }
+
+ std::cout << "RealSense摄像头初始化成功: "
+ << config_.width << "x" << config_.height << "@" << config_.fps << "fps" << std::endl;
+
+ return 0;
+
+ } catch (const rs2::error& e) {
+ std::cerr << "RealSense错误: " << e.what() << std::endl;
+ return -1;
+ } catch (const std::exception& e) {
+ std::cerr << "常规错误: " << e.what() << std::endl;
+ return -1;
+ }
+}
+
+int RealSenseCapture::init_device() {
+ try {
+ // 查询设备
+ rs2::device_list devices = ctx_.query_devices();
+ size_t device_count = devices.size();
+
+ if (device_count == 0) {
+ std::cerr << "未检测到RealSense设备" << std::endl;
+ return -1;
+ }
+
+ std::cout << "检测到 " << device_count << " 个RealSense设备" << std::endl;
+
+ // 如果指定了序列号,查找对应设备
+ if (!config_.serial_number.empty()) {
+ bool found = false;
+ for (size_t i = 0; i < device_count; i++) {
+ rs2::device dev = devices[i];
+ std::string serial = dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER);
+
+ if (serial == config_.serial_number) {
+ std::cout << "找到指定序列号的设备: " << serial << std::endl;
+ found = true;
+ rs_cfg_.enable_device(serial);
+ break;
+ }
+ }
+
+ if (!found) {
+ std::cerr << "未找到序列号为 " << config_.serial_number << " 的设备" << std::endl;
+ return -1;
+ }
+ } else {
+ // 使用第一个设备
+ rs2::device dev = devices[0];
+ std::string serial = dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER);
+ std::cout << "使用第一个设备: " << dev.get_info(RS2_CAMERA_INFO_NAME)
+ << " (序列号: " << serial << ")" << std::endl;
+ rs_cfg_.enable_device(serial);
+ }
+
+ // 配置彩色视频流
+ rs_cfg_.enable_stream(RS2_STREAM_COLOR,
+ config_.width,
+ config_.height,
+ RS2_FORMAT_BGR8, // OpenCV使用BGR格式
+ config_.fps);
+
+ return 0;
+
+ } catch (const rs2::error& e) {
+ std::cerr << "初始化设备失败: " << e.what() << std::endl;
+ return -1;
+ }
+}
+
+AVFrame* RealSenseCapture::create_avframe(int width, int height, AVPixelFormat pix_fmt) {
+ AVFrame* frame = av_frame_alloc();
+ if (!frame) {
+ return nullptr;
+ }
+
+ frame->width = width;
+ frame->height = height;
+ frame->format = pix_fmt;
+
+ // 分配缓冲区
+ int ret = av_frame_get_buffer(frame, 0);
+ if (ret < 0) {
+ av_frame_free(&frame);
+ return nullptr;
+ }
+
+ return frame;
+}
+
+AVFrame* RealSenseCapture::realsense_to_avframe(rs2::frame frame) {
+ auto vf = frame.as();
+ const int width = vf.get_width();
+ const int height = vf.get_height();
+ const uint8_t* data = reinterpret_cast(vf.get_data());
+
+ // 确保帧可写
+ av_frame_make_writable(frame_);
+
+ // RealSense的BGR8格式,linesize = width * 3
+ int src_linesize = width * 3;
+
+ // 复制数据到AVFrame
+ for (int i = 0; i < height; i++) {
+ memcpy(frame_->data[0] + i * frame_->linesize[0],
+ data + i * src_linesize,
+ src_linesize);
+ }
+
+ return frame_;
+}
+
+cv::Mat RealSenseCapture::realsense_to_cvmat(rs2::frame frame) {
+ auto vf = frame.as();
+ const int width = vf.get_width();
+ const int height = vf.get_height();
+
+ // RealSense的BGR8格式直接对应OpenCV的CV_8UC3
+ cv::Mat image(cv::Size(width, height), CV_8UC3, (void*)vf.get_data(), cv::Mat::AUTO_STEP);
+
+ // 返回深拷贝,确保图像数据独立
+ return image.clone();
+}
+
+int RealSenseCapture::start_capture(FrameCallback callback) {
+ std::lock_guard lock(mutex_);
+
+ if (!callback) {
+ std::cerr << "回调函数为空" << std::endl;
+ return -1;
+ }
+
+ if (is_capturing_) {
+ std::cerr << "已经在捕获中" << std::endl;
+ return -1;
+ }
+
+ try {
+ // 启动pipeline
+ profile_ = pipe_.start(rs_cfg_);
+
+ // 获取内参
+ auto stream = profile_.get_stream(RS2_STREAM_COLOR).as();
+ intrinsics_ = stream.get_intrinsics();
+
+ // 等待第一帧,确保设备正常工作
+ rs2::frameset frames = pipe_.wait_for_frames(2000); // 2秒超时
+ if (!frames.get_color_frame()) {
+ std::cerr << "无法获取第一帧" << std::endl;
+ pipe_.stop();
+ return -1;
+ }
+
+ is_capturing_ = true;
+ frame_count_ = 0;
+
+ // 启动捕获线程
+ capture_thread_ = std::make_unique(&RealSenseCapture::capture_thread_func,
+ this, callback);
+
+ std::cout << "RealSense开始采集" << std::endl;
+ return 0;
+
+ } catch (const rs2::error& e) {
+ std::cerr << "启动采集失败: " << e.what() << std::endl;
+ return -1;
+ }
+}
+
+void RealSenseCapture::capture_thread_func(FrameCallback callback) {
+ const int frame_interval = 1000 / config_.fps; // 毫秒
+
+ while (is_capturing_) {
+ auto frame_start = std::chrono::steady_clock::now();
+
+ try {
+ // 等待帧数据
+ rs2::frameset frames = pipe_.wait_for_frames(1000); // 1秒超时
+ rs2::frame color_frame = frames.get_color_frame();
+
+ if (!color_frame) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ continue;
+ }
+
+ // 转换为AVFrame(用于编码)
+ AVFrame* av_frame = realsense_to_avframe(color_frame);
+ av_frame->pts = frame_count_;
+
+ // 转换为cv::Mat(用于显示或OpenCV处理)
+ cv::Mat rgb_image = realsense_to_cvmat(color_frame);
+
+ // 回调:同时返回AVFrame和cv::Mat
+ callback(av_frame, rgb_image, frame_count_++);
+
+ } catch (const rs2::error& e) {
+ std::cerr << "采集错误: " << e.what() << std::endl;
+ if (!is_capturing_) break;
+ }
+
+ // 控制帧率
+ auto frame_end = std::chrono::steady_clock::now();
+ auto elapsed = std::chrono::duration_cast(
+ frame_end - frame_start).count();
+
+ if (elapsed < frame_interval) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(frame_interval - elapsed));
+ }
+ }
+}
+
+void RealSenseCapture::stop_capture() {
+ std::lock_guard lock(mutex_);
+
+ if (!is_capturing_) {
+ return;
+ }
+
+ is_capturing_ = false;
+
+ if (capture_thread_ && capture_thread_->joinable()) {
+ capture_thread_->join();
+ capture_thread_.reset();
+ }
+
+ try {
+ pipe_.stop();
+ } catch (const std::exception& e) {
+ std::cerr << "停止pipeline时出错: " << e.what() << std::endl;
+ }
+
+ std::cout << "RealSense停止采集" << std::endl;
+}
+
+std::string RealSenseCapture::get_device_info() const {
+ try {
+ rs2::device_list devices = ctx_.query_devices();
+ if (devices.size() == 0) {
+ return "No RealSense device found";
+ }
+
+ std::string info;
+ for (size_t i = 0; i < devices.size(); i++) {
+ rs2::device dev = devices[i];
+ info += "Device " + std::to_string(i + 1) + ": ";
+ info += dev.get_info(RS2_CAMERA_INFO_NAME);
+ info += " (SN: " + std::string(dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER)) + ")";
+ if (i < devices.size() - 1) info += "\n";
+ }
+ return info;
+
+ } catch (const std::exception& e) {
+ return "Failed to get device info: " + std::string(e.what());
+ }
+}
+
+} // namespace ffmpeg
\ No newline at end of file
diff --git a/cmvr-es/devices/camera/realsense_camera/src/realsense_camera.cpp b/cmvr-es/devices/camera/realsense_camera/src/realsense_camera.cpp
index 43aa315d..ec616665 100644
--- a/cmvr-es/devices/camera/realsense_camera/src/realsense_camera.cpp
+++ b/cmvr-es/devices/camera/realsense_camera/src/realsense_camera.cpp
@@ -218,10 +218,10 @@ void RealsenseCamera::init() {
}
// 初始化深度图编码器(示例参数:640x480,15fps,H.265)
- if (!initSingleEncoder(depthEncoder_, codec_, width_, height_, fps_)) {
- LOG(ERROR) << "[RealsenseCamera] (start): Failed to init depth encoder!";
- throw runtime_error("Failed to init depth encoder!");
- }
+ // if (!initSingleEncoder(depthEncoder_, codec_, width_, height_, fps_)) {
+ // LOG(ERROR) << "[RealsenseCamera] (start): Failed to init depth encoder!";
+ // throw runtime_error("Failed to init depth encoder!");
+ // }
LOG(INFO) << "[RealsenseCamera] (init): realsense camera initialized at index " << serial_; // 记录初始化成功日志
}
catch (const exception &error){
@@ -884,7 +884,7 @@ bool RealsenseCamera::initSingleEncoder(std::shared_ptr& enco
ctx->time_base = {1, fps}; // 时间基(1/fps)
ctx->framerate = {fps, 1}; // 帧率
ctx->max_b_frames = 0; // 禁用B帧(降低延迟)
- ctx->gop_size = 10;//I帧间隔1,每一帧都是I帧
+ ctx->gop_size = 10;//I帧间隔
// 5. 设置编码器私有参数
if (codec->id == AV_CODEC_ID_H264) {
av_opt_set(ctx->priv_data, "preset", "ultrafast", 0);
@@ -895,6 +895,24 @@ bool RealsenseCamera::initSingleEncoder(std::shared_ptr& enco
} else if (codec->id == AV_CODEC_ID_HEVC) {
// 降低 x265 控制台日志噪声(如 "encoded 0 frames")。
av_opt_set(ctx->priv_data, "x265-params", "log-level=none", 0);
+ // H.265 的强制设置
+ // 使用 x265-params 字符串设置所有参数
+ char x265_params[256];
+ snprintf(x265_params, sizeof(x265_params),
+ "keyint=%d:" // 关键帧间隔
+ "min-keyint=%d:" // 最小关键帧间隔
+ "no-open-gop=1:" // 禁用开放GOP
+ "bframes=0:" // 禁用B帧
+ "lookahead=0:"
+ "log-level=none", // 日志级别
+ 10, 10); // 设置keyint和min-keyint为10
+
+ av_opt_set(ctx->priv_data, "x265-params", x265_params, 0);
+
+ // 或者分开设置(如果支持)
+ av_opt_set_int(ctx->priv_data, "keyint", 10, 0);
+ av_opt_set_int(ctx->priv_data, "min-keyint", 10, 0);
+ av_opt_set(ctx->priv_data, "no-open-gop", "1", 0);
}
// 6. 设置像素格式(不变)
@@ -938,6 +956,17 @@ bool RealsenseCamera::initSingleEncoder(std::shared_ptr& enco
return false;
}
+ // 添加调试信息,确认设置生效
+ std::cout << "Encoder settings:" << std::endl;
+ std::cout << " GOP size: " << ctx->gop_size << std::endl;
+ if (codec->id == AV_CODEC_ID_HEVC) {
+ char* params = nullptr;
+ if (av_opt_get(ctx->priv_data, "x265-params", 0, (uint8_t**)¶ms) >= 0) {
+ std::cout << " x265-params: " << params << std::endl;
+ av_free(params);
+ }
+ }
+
std::cout << "Successfully initialized " << codec_name << " encoder ( "
<< width << "x" << height << "@" << fps << "fps )" << std::endl;
return true;
diff --git a/cmvr-es/devices/robot/abstract_robot.h b/cmvr-es/devices/robot/abstract_robot.h
index 823caf44..29ffb6f8 100644
--- a/cmvr-es/devices/robot/abstract_robot.h
+++ b/cmvr-es/devices/robot/abstract_robot.h
@@ -176,6 +176,9 @@ namespace cmvr::device{
virtual msgs::Pose3d fk(const std::string &base_link, const std::string &ee_link) = 0;
virtual std::vector ik(const std::string &base_link, const std::string &ee_link,msgs::Pose3d pose) = 0;
+
+ virtual bool getPoseMatrix(std::string &bask_link, std::string &ee_link, Eigen::Matrix4d& cur_pose) = 0;
+ virtual bool fk(const std::vector& joints_angle, Eigen::Matrix4d& cur_pose, bool is_tcp = true) = 0;
protected:
int dof_{};
RobotState state_{};
diff --git a/cmvr-es/service/grpc/include/grpc_humanoid_robot_service.h b/cmvr-es/service/grpc/include/grpc_humanoid_robot_service.h
index a8a404ea..b9f0b9f9 100644
--- a/cmvr-es/service/grpc/include/grpc_humanoid_robot_service.h
+++ b/cmvr-es/service/grpc/include/grpc_humanoid_robot_service.h
@@ -6,7 +6,7 @@
#include "device_manager/include/device_manager.h"
#include "cmvr/api/humanoid_robot_service.grpc.pb.h"
-
+#include "ik_solver/include/pinocchio_qp_ik_solver.h"
namespace cmvr {
namespace service {
class gRPCHumanoidRobotServiceImpl final : public api::HumanoidRobotService::Service {
@@ -31,8 +31,19 @@ namespace cmvr {
grpc::Status getPose(grpc::ServerContext *context, const cmvr::api::GetPose_Request *request, cmvr::api::GetPose_Response *response) override;
grpc::Status calibrateZeroQ(grpc::ServerContext* context, const cmvr::api::CalibrateZeroQ_Request* request, cmvr::api::CalibrateZeroQ_Response* response) override;
+
+ grpc::Status servoJ(grpc::ServerContext* context, const cmvr::api::ServoJ_Request* request, cmvr::api::ServoJ_Response* response) override;
+
+ grpc::Status getPoseMatrix(grpc::ServerContext* context, const cmvr::api::GetPoseMatrix_Request* request, cmvr::api::GetPoseMatrix_Response* response) override;
+
+ grpc::Status computeForwardKinematics(grpc::ServerContext* context, const cmvr::api::ComputeForwardKinematics_Request* request, cmvr::api::ComputeForwardKinematics_Response* response) override;
private:
device::DeviceManager& dmgr_;
+
+
+
+ //矩阵转换
+ std::shared_ptr pinocchio_ik_{nullptr};
};
diff --git a/cmvr-es/service/grpc/src/grpc_camera_service.cpp b/cmvr-es/service/grpc/src/grpc_camera_service.cpp
index ce34dc9d..fb033cc0 100644
--- a/cmvr-es/service/grpc/src/grpc_camera_service.cpp
+++ b/cmvr-es/service/grpc/src/grpc_camera_service.cpp
@@ -95,9 +95,9 @@ grpc::Status gRPCCameraServiceImpl::GetRGBImage(grpc::ServerContext* context,
response->mutable_header()->set_success(true);
response->mutable_intrinsics()->set_fx(intrinsics.fx);
- response->mutable_intrinsics()->set_fy(intrinsics.fx);
- response->mutable_intrinsics()->set_cx(intrinsics.fx);
- response->mutable_intrinsics()->set_cy(intrinsics.fx);
+ response->mutable_intrinsics()->set_fy(intrinsics.fy);
+ response->mutable_intrinsics()->set_cx(intrinsics.cx);
+ response->mutable_intrinsics()->set_cy(intrinsics.cy);
for (int i = 0; i < 5 ; i++) {
response->mutable_intrinsics()->add_coeffs(intrinsics.coeffs[i]);
}
@@ -146,9 +146,9 @@ grpc::Status gRPCCameraServiceImpl::GetDepthImage(grpc::ServerContext* context,
response->mutable_header()->set_success(true);
response->mutable_intrinsics()->set_fx(intrinsics.fx);
- response->mutable_intrinsics()->set_fy(intrinsics.fx);
- response->mutable_intrinsics()->set_cx(intrinsics.fx);
- response->mutable_intrinsics()->set_cy(intrinsics.fx);
+ response->mutable_intrinsics()->set_fy(intrinsics.fy);
+ response->mutable_intrinsics()->set_cx(intrinsics.cx);
+ response->mutable_intrinsics()->set_cy(intrinsics.cy);
for (int i = 0; i < 5 ; i++) {
response->mutable_intrinsics()->add_coeffs(intrinsics.coeffs[i]);
}
@@ -200,9 +200,9 @@ grpc::Status gRPCCameraServiceImpl::GetRGBDImages(grpc::ServerContext* context,
response->mutable_header()->set_success(true);
response->mutable_intrinsics()->set_fx(intrinsics.fx);
- response->mutable_intrinsics()->set_fy(intrinsics.fx);
- response->mutable_intrinsics()->set_cx(intrinsics.fx);
- response->mutable_intrinsics()->set_cy(intrinsics.fx);
+ response->mutable_intrinsics()->set_fy(intrinsics.fy);
+ response->mutable_intrinsics()->set_cx(intrinsics.cx);
+ response->mutable_intrinsics()->set_cy(intrinsics.cy);
for (int i = 0; i < 5 ; i++) {
response->mutable_intrinsics()->add_coeffs(intrinsics.coeffs[i]);
}
@@ -454,9 +454,9 @@ grpc::Status gRPCCameraServiceImpl::GetRGBImageStream(grpc::ServerContext* conte
response.mutable_color_frame()->set_height(frame_data.height);
response.mutable_intrinsics()->set_fx(frame_data.intrinsics.fx);
- response.mutable_intrinsics()->set_fy(frame_data.intrinsics.fx);
- response.mutable_intrinsics()->set_cx(frame_data.intrinsics.fx);
- response.mutable_intrinsics()->set_cy(frame_data.intrinsics.fx);
+ response.mutable_intrinsics()->set_fy(frame_data.intrinsics.fy);
+ response.mutable_intrinsics()->set_cx(frame_data.intrinsics.cx);
+ response.mutable_intrinsics()->set_cy(frame_data.intrinsics.cy);
for (int i = 0; i < 5 ; i++) {
response.mutable_intrinsics()->add_coeffs(frame_data.intrinsics.coeffs[i]);
}
diff --git a/cmvr-es/service/grpc/src/grpc_humanoid_robot_service.cpp b/cmvr-es/service/grpc/src/grpc_humanoid_robot_service.cpp
index f121f1dd..5cade894 100644
--- a/cmvr-es/service/grpc/src/grpc_humanoid_robot_service.cpp
+++ b/cmvr-es/service/grpc/src/grpc_humanoid_robot_service.cpp
@@ -14,7 +14,14 @@ using namespace cmvr::api;
using google::protobuf::util::TimeUtil;
-gRPCHumanoidRobotServiceImpl::gRPCHumanoidRobotServiceImpl():dmgr_(DeviceManager::getInstance()){}
+gRPCHumanoidRobotServiceImpl::gRPCHumanoidRobotServiceImpl():dmgr_(DeviceManager::getInstance())
+{
+
+ pinocchio_ik_ = std::make_shared("/home/linbo/cmvr/cmvr-es/model/xiaoyan_description/dual_arm.urdf"
+ ,"PELVIS_S"
+ ,"R_WRIST_R_S");
+ pinocchio_ik_->init();
+}
grpc::Status gRPCHumanoidRobotServiceImpl::torqueOff(grpc::ServerContext *context,
@@ -244,3 +251,182 @@ grpc::Status gRPCHumanoidRobotServiceImpl::calibrateZeroQ(grpc::ServerContext* c
*response->mutable_header()->mutable_timestamp() = TimeUtil::GetCurrentTime();
return ret;
}
+
+
+grpc::Status gRPCHumanoidRobotServiceImpl::servoJ(grpc::ServerContext* context
+ , const cmvr::api::ServoJ_Request* request
+ , cmvr::api::ServoJ_Response* response)
+{
+ grpc::Status ret = grpc::Status::OK;
+ try {
+ auto robot = dmgr_.getDevice(request->header().device_id());
+ std::vector cmds{};
+ for (const auto& jc : request->cmds()) {
+ cmds.emplace_back(jc.joint_name(),jc.rad(),jc.vel());
+ }
+ robot->servoJ(cmds,request->vel(),0);
+
+ response->mutable_header()->set_success(true);
+ response->mutable_header()->set_error_message("");
+ }catch (const std::exception& e) {
+ response->mutable_header()->set_success(false);
+ response->mutable_header()->set_error_message(e.what());
+ ret = grpc::Status(grpc::StatusCode::INTERNAL, e.what());
+ }
+ *response->mutable_header()->mutable_timestamp() = TimeUtil::GetCurrentTime();
+ return ret;
+
+}
+
+
+// 在 gRPC 服务实现文件中添加以下代码
+
+grpc::Status gRPCHumanoidRobotServiceImpl::getPoseMatrix(grpc::ServerContext* context
+ , const cmvr::api::GetPoseMatrix_Request* request
+ , cmvr::api::GetPoseMatrix_Response* response)
+{
+ grpc::Status ret = grpc::Status::OK;
+ try {
+ // 获取机械臂设备
+ auto robot = dmgr_.getDevice(request->header().device_id());
+
+ // 获取请求参数
+ std::string base_link = request->base_link();
+ std::string ee_link = request->ee_link();
+
+ // 调用机械臂的 getPoseMatrix 方法获取当前位姿矩阵
+ Eigen::Matrix4d cur_pose;
+ bool success = robot->getPoseMatrix(base_link, ee_link, cur_pose);
+
+ if (success) {
+ // 设置成功状态
+ response->mutable_header()->set_success(true);
+ response->mutable_header()->set_error_message("");
+
+ // 填充4x4矩阵到响应
+ auto* matrix = response->mutable_matrix();
+ matrix->set_m00(cur_pose(0, 0));
+ matrix->set_m01(cur_pose(0, 1));
+ matrix->set_m02(cur_pose(0, 2));
+ matrix->set_m03(cur_pose(0, 3));
+
+ matrix->set_m10(cur_pose(1, 0));
+ matrix->set_m11(cur_pose(1, 1));
+ matrix->set_m12(cur_pose(1, 2));
+ matrix->set_m13(cur_pose(1, 3));
+
+ matrix->set_m20(cur_pose(2, 0));
+ matrix->set_m21(cur_pose(2, 1));
+ matrix->set_m22(cur_pose(2, 2));
+ matrix->set_m23(cur_pose(2, 3));
+
+ matrix->set_m30(cur_pose(3, 0));
+ matrix->set_m31(cur_pose(3, 1));
+ matrix->set_m32(cur_pose(3, 2));
+ matrix->set_m33(cur_pose(3, 3));
+
+ // 可选:记录日志
+ // LOG(INFO) << "getPoseMatrix success for device: " << request->header().device_id();
+ } else {
+ // 获取位姿失败
+ response->mutable_header()->set_success(false);
+ response->mutable_header()->set_error_message("Failed to get pose matrix");
+ ret = grpc::Status(grpc::StatusCode::INTERNAL, "Failed to get pose matrix");
+ }
+
+ } catch (const std::exception& e) {
+ response->mutable_header()->set_success(false);
+ response->mutable_header()->set_error_message(e.what());
+ ret = grpc::Status(grpc::StatusCode::INTERNAL, e.what());
+ }
+
+ // 设置时间戳
+ *response->mutable_header()->mutable_timestamp() = TimeUtil::GetCurrentTime();
+ return ret;
+}
+
+
+grpc::Status gRPCHumanoidRobotServiceImpl::computeForwardKinematics(grpc::ServerContext* context
+ , const cmvr::api::ComputeForwardKinematics_Request* request
+ , cmvr::api::ComputeForwardKinematics_Response* response)
+{
+ grpc::Status ret = grpc::Status::OK;
+ try {
+ // // 获取机械臂设备
+ // auto robot = dmgr_.getDevice(request->header().device_id());
+
+ // 解析关节命令
+ std::vector cmds{};
+ for (const auto& jc : request->joints()) {
+ cmds.emplace_back(jc.joint_name(), jc.rad(), jc.vel());
+ }
+
+ // 如果没有关节命令,返回错误
+ if (cmds.empty()) {
+ response->mutable_header()->set_success(false);
+ response->mutable_header()->set_error_message("No joint commands provided");
+ *response->mutable_header()->mutable_timestamp() = TimeUtil::GetCurrentTime();
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "No joint commands provided");
+ }
+
+ // 获取请求参数
+ std::string base_link = request->base_link();
+ std::string ee_link = request->ee_link();
+ bool is_tcp = true; // 默认使用TCP
+
+ // 从关节命令中提取关节角度
+ std::vector joints_angle;
+ for (const auto& cmd : cmds) {
+ joints_angle.push_back(cmd.rad); // 使用 rad 作为关节角度
+ }
+
+ // 调用机械臂的 fk 方法计算正运动学
+ Eigen::Matrix4d cur_pose;
+ bool success = pinocchio_ik_->fk(joints_angle, cur_pose, is_tcp);
+
+ if (success) {
+ // 设置成功状态
+ response->mutable_header()->set_success(true);
+ response->mutable_header()->set_error_message("");
+
+ // 填充4x4矩阵到响应
+ auto* matrix = response->mutable_matrix();
+ matrix->set_m00(cur_pose(0, 0));
+ matrix->set_m01(cur_pose(0, 1));
+ matrix->set_m02(cur_pose(0, 2));
+ matrix->set_m03(cur_pose(0, 3));
+
+ matrix->set_m10(cur_pose(1, 0));
+ matrix->set_m11(cur_pose(1, 1));
+ matrix->set_m12(cur_pose(1, 2));
+ matrix->set_m13(cur_pose(1, 3));
+
+ matrix->set_m20(cur_pose(2, 0));
+ matrix->set_m21(cur_pose(2, 1));
+ matrix->set_m22(cur_pose(2, 2));
+ matrix->set_m23(cur_pose(2, 3));
+
+ matrix->set_m30(cur_pose(3, 0));
+ matrix->set_m31(cur_pose(3, 1));
+ matrix->set_m32(cur_pose(3, 2));
+ matrix->set_m33(cur_pose(3, 3));
+
+ // 可选:记录日志
+ // LOG(INFO) << "computeForwardKinematics success for device: " << request->header().device_id();
+ } else {
+ // 正运动学计算失败
+ response->mutable_header()->set_success(false);
+ response->mutable_header()->set_error_message("Forward kinematics calculation failed");
+ ret = grpc::Status(grpc::StatusCode::INTERNAL, "Forward kinematics calculation failed");
+ }
+
+ } catch (const std::exception& e) {
+ response->mutable_header()->set_success(false);
+ response->mutable_header()->set_error_message(e.what());
+ ret = grpc::Status(grpc::StatusCode::INTERNAL, e.what());
+ }
+
+ // 设置时间戳
+ *response->mutable_header()->mutable_timestamp() = TimeUtil::GetCurrentTime();
+ return ret;
+}
\ No newline at end of file
diff --git a/protos/cmvr/api/humanoid_robot_command.proto b/protos/cmvr/api/humanoid_robot_command.proto
index f39ad778..75804fb1 100644
--- a/protos/cmvr/api/humanoid_robot_command.proto
+++ b/protos/cmvr/api/humanoid_robot_command.proto
@@ -40,6 +40,17 @@ enum RobotJointIndexDirection{
ROTATE_Y = 9; // 绕Y轴旋转
ROTATE_Z = 10; // 绕Z轴旋转
}
+
+
+// 4x4 齐次变换矩阵
+message TransformMatrix4x4 {
+ double m00 = 1; double m01 = 2; double m02 = 3; double m03 = 4;
+ double m10 = 5; double m11 = 6; double m12 = 7; double m13 = 8;
+ double m20 = 9; double m21 = 10; double m22 = 11; double m23 = 12;
+ double m30 = 13; double m31 = 14; double m32 = 15; double m33 = 16;
+}
+
+
message MoveJ{
message Request{
CommandHeader.Request header = 1;
@@ -144,3 +155,48 @@ message CalibrateZeroQ {
CommandHeader.Feedback header= 1;
}
}
+
+
+message ServoJ{
+ message Request{
+ CommandHeader.Request header = 1;
+ repeated JointCmd cmds = 2;
+ double vel = 3;
+ }
+
+ message Response{
+ CommandHeader.Feedback header= 1;
+ }
+
+}
+
+
+
+// 获取当前位姿的4x4矩阵
+message GetPoseMatrix {
+ message Request {
+ CommandHeader.Request header = 1;
+ string base_link = 2; // 基坐标系
+ string ee_link = 3; // 末端连杆名称
+ }
+
+ message Response {
+ CommandHeader.Feedback header = 1;
+ TransformMatrix4x4 matrix = 2; // 4x4 位姿矩阵
+ }
+}
+
+// 根据关节角度计算正运动学位姿矩阵
+message ComputeForwardKinematics {
+ message Request {
+ CommandHeader.Request header = 1;
+ string base_link = 2; // 基坐标系
+ string ee_link = 3; // 末端连杆名称
+ repeated JointCmd joints = 4; // 关节角度
+ }
+
+ message Response {
+ CommandHeader.Feedback header = 1;
+ TransformMatrix4x4 matrix = 2; // 计算出的4x4位姿矩阵
+ }
+}
\ No newline at end of file
diff --git a/protos/cmvr/api/humanoid_robot_service.proto b/protos/cmvr/api/humanoid_robot_service.proto
index 33502e50..b046eb92 100644
--- a/protos/cmvr/api/humanoid_robot_service.proto
+++ b/protos/cmvr/api/humanoid_robot_service.proto
@@ -15,4 +15,8 @@ service HumanoidRobotService{
rpc getJointState(JointRequest) returns (JointResponse);
rpc getPose(GetPose.Request) returns (GetPose.Response);
rpc calibrateZeroQ(CalibrateZeroQ.Request) returns (CalibrateZeroQ.Response);
+ rpc servoJ(ServoJ.Request) returns (ServoJ.Response);
+
+ rpc getPoseMatrix(GetPoseMatrix.Request) returns (GetPoseMatrix.Response);
+ rpc computeForwardKinematics(ComputeForwardKinematics.Request) returns (ComputeForwardKinematics.Response);
}