2025.8.29机械臂实战项目

        好久没给大家更新了,上周末大学大四开学,所以停更了几天,回来后在做项目,接下来的几篇文章,给大家带来几个项目,第一个介绍的是机械臂操作,说是机械臂操作,简单来说,就是tcp网络通信,完成相应的指令。

        任务要求是:

        1)基于TCP服务器的机械臂,端口号可指定暂时为8888, ip是Windows的ip;

        查看Windows的IP:按住Windows+r 按键,输入cmd , 输入ipconfig

        2)点击软件中的开启监听;

        3)机械臂需要发送16进制数,共5个字节,协议如下

        0xff 0x02 x y 0xff

        0xff:起始结束协议,固定的;

        0x02:控制机械手臂协议,固定的;

        x:指定要操作的机械臂 0x00 红色摆臂 0x01 蓝色摆臂

        y:指定角度

        机械臂的小应用如下图,因为涉及版权问题,先不贴给大家了,可以私信

        首先我们来看一下完成这个任务需要怎么做,初始化阶段,核心是建立客户端与服务器的通信链路。先通过socket()函数创建 TCP 套接字(负责数据传输的 “通道”),若创建失败则报错退出;接着定义服务器的网络信息(IP、端口、协议族),并通过connect()函数与目标服务器建立连接,确保后续指令能准确送达;同时初始化红 / 蓝臂的初始角度(红 0°、蓝 90°),并向用户提示控制按键规则与角度范围,为后续操作铺垫。
然后是核心交互阶段,以 “用户输入 - 角度计算 - 指令封装 - 数据发送” 为循环逻辑。通过getchar()获取用户按键(w/s/d/a/q),先处理退出逻辑(按 q 则关闭连接并退出);若为控制按键,则先更新对应机械臂的角度(红臂 ±1°、蓝臂 ±1°),并强制将角度限制在规定范围(红 - 90°~90°、蓝 0°~180°);再按固定协议封装 5 字节指令(起始符 0xff + 类型 0x02 + 臂标识 + 角度 + 结束符 0xff),最后通过send()将指令发送给服务器,完成一次控制;若输入无效按键,则提示用户重新输入。
最后是收尾阶段,当用户按 q 退出循环后,通过close()关闭之前创建的 TCP 套接字,释放网络资源,确保程序优雅退出,避免资源泄漏。

#include <myhead.h>  // 自定义头文件,通常包含标准库和项目通用定义// 宏定义服务器的端口号和IP地址
#define PORT 8888       // 服务器监听的端口号
#define IP "192.168.0.74"  // 服务器的IP地址int main(int argc, const char *argv[])
{char key;             // 存储用户输入的控制按键char buff[5];         // 用于发送数据的缓冲区,长度为5字节// 创建TCP套接字:AF_INET表示IPv4协议,SOCK_STREAM表示TCP协议,0表示自动选择协议int oldfd = socket(AF_INET, SOCK_STREAM, 0);// 检查套接字是否创建成功if (oldfd == -1){perror("socket");  // 打印错误信息return -1;         // 创建失败,退出程序}// 定义服务器的网络地址结构struct sockaddr_in server = {.sin_family = AF_INET,                // 使用IPv4地址族.sin_port = htons(PORT),              // 将端口号转换为网络字节序.sin_addr.s_addr = inet_addr(IP)      // 将字符串IP转换为网络字节序};// 连接到服务器if (connect(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1){perror("connect");  // 打印连接失败信息return -1;          // 连接失败,退出程序}// 连接成功后,打印服务器信息和操作提示printf("已成功连接了服务器%s-%d\n", inet_ntoa(server.sin_addr), PORT);printf("控制命令为:w--(红色臂顺时针+1°) s--(红色臂逆时针-1°) d--(蓝色臂顺时针+1°) a--(蓝色臂逆时针-1°)\n");printf("角度范围:红色臂(-90°~90°)  蓝色臂(0°~180°)\n");printf("按q键退出程序\n");// 初始化机械臂角度int red_angle = 0;    // 红色臂初始角度设为0°int blue_angle = 90;  // 蓝色臂初始角度设为90°// 主控制循环:持续接收用户输入并发送控制命令while (1){printf("\n请输入控制按键:");key = getchar();               // 获取用户输入的按键while(getchar()!='\n');        // 清空输入缓冲区,避免残留字符影响下次输入// 检查是否退出程序if (key == 'q'){printf("退出程序...\n");break;  // 跳出循环,结束程序}// 初始化数据缓冲区(通信协议格式)buff[0] = 0xff;  // 帧头标志buff[1] = 0x02;  // 数据类型或长度标识buff[4] = 0xff;  // 帧尾标志// 根据用户输入的按键执行相应操作switch (key){case 'w':  // 红色臂顺时针旋转+1°red_angle += 1;// 限制角度在有效范围内if(red_angle > 90){red_angle = 90;  // 超过最大角度,强制设为最大值}else if(red_angle < -90) {red_angle = -90; // 小于最小角度,强制设为最小值}buff[2] = 0x00;     // 0x00表示控制红色臂buff[3] = red_angle; // 存储当前角度值printf("红色臂角度更新:%d°\n", red_angle);break;case 's':  // 红色臂逆时针旋转-1°red_angle -= 1;// 限制角度在有效范围内if(red_angle > 90){red_angle = 90;}else if(red_angle < -90){red_angle = -90;}buff[2] = 0x00;     // 0x00表示控制红色臂buff[3] = red_angle; // 存储当前角度值printf("红色臂角度更新:%d°\n", red_angle);break;case 'd':  // 蓝色臂顺时针旋转+1°blue_angle += 1;// 限制角度在有效范围内if(blue_angle > 180){blue_angle = 180;}else if(blue_angle < 0){blue_angle = 0;}buff[2] = 0x01;     // 0x01表示控制蓝色臂buff[3] = blue_angle;// 存储当前角度值printf("蓝色臂角度更新:%d°\n", blue_angle);break;case 'a':  // 蓝色臂逆时针旋转-1°blue_angle -= 1;// 限制角度在有效范围内if(blue_angle > 180) {blue_angle = 180;}else if(blue_angle < 0){blue_angle = 0;}buff[2] = 0x01;     // 0x01表示控制蓝色臂buff[3] = blue_angle;// 存储当前角度值printf("蓝色臂角度更新:%d°\n", blue_angle);break;default:  // 处理无效输入printf("无效按键!请重新输入(w/s/d/a/q)\n");continue;  // 跳过本次循环,不发送数据}// 发送数据到服务器int res = send(oldfd, buff, 5, 0);if (res == -1)  // 检查发送是否成功{perror("send");    // 打印发送失败信息close(oldfd);      // 关闭套接字return -1;         // 退出程序}}// 关闭套接字,释放资源close(oldfd);return 0;
}

        我的这个代码主要以单次命令为主,即输入wads回车完成对机械臂红蓝色机械臂的控制,在完善代码的过程中,我发现红蓝色机械臂的角度受限红色机械臂的可调角度为-90°-90°,而蓝色机械臂可调角度为0-180°所以我加了个限制,使角度始终为这个区间内,来看一下效果。

        先看一下自己电脑现在的IP地址

        随后我发现这样一次次的操作再加上回车太过麻烦,效率非常低,我随后通过init_curses()函数初始化终端为无缓冲、无回显模式,支持实时按键响应(无需按回车确认)

#include <myhead.h>
#include <curses.h>  // 包含curses库
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define PORT 8888
#define IP "192.168.0.74"// 初始化curses模式
void init_curses() {initscr();          // 初始化屏幕cbreak();           // 关闭行缓冲,按键直接生效noecho();           // 关闭输入回显keypad(stdscr, TRUE); // 启用特殊按键支持refresh();          // 刷新屏幕
}// 显示程序信息和控制说明
void show_info(int red_angle, int blue_angle) {clear();            // 清空屏幕// 显示标题和连接信息mvprintw(1, 2, "===== 机械臂控制系统 =====");mvprintw(3, 2, "已连接服务器: %s:%d", IP, PORT);// 显示当前角度信息mvprintw(5, 2, "当前角度:");mvprintw(6, 4, "红色臂: %d° (范围: -90° ~ 90°)", red_angle);mvprintw(7, 4, "蓝色臂: %d° (范围: 0° ~ 180°)", blue_angle);// 显示控制说明mvprintw(9, 2, "控制命令:");mvprintw(10, 4, "w: 红色臂顺时针 (+1°)");mvprintw(11, 4, "s: 红色臂逆时针 (-1°)");mvprintw(12, 4, "d: 蓝色臂顺时针 (+1°)");mvprintw(13, 4, "a: 蓝色臂逆时针 (-1°)");mvprintw(14, 4, "q: 退出程序");// 显示状态提示mvprintw(16, 2, "状态: 就绪 (按任意控制键操作)");mvprintw(17, 2, "----------------------------------------");refresh();          // 刷新屏幕显示
}// 显示操作结果提示
void show_status(const char *msg) {mvprintw(16, 2, "状态: %s", msg);  // 在固定位置显示状态mvprintw(18, 2, "按任意键继续...");refresh();getch();            // 等待按键继续
}int main(int argc, const char *argv[]) {char key;char buff[5]; int oldfd = socket(AF_INET, SOCK_STREAM, 0);int red_angle = 0;    // 红色臂初始角度int blue_angle = 90;  // 蓝色臂初始角度// 初始化cursesinit_curses();// 创建socketif (oldfd == -1) {endwin();  // 退出curses模式perror("socket创建失败");return -1;}// 设置服务器地址struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};// 连接服务器if (connect(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1) {endwin();  // 退出curses模式perror("连接服务器失败");close(oldfd);return -1;}// 显示初始界面show_info(red_angle, blue_angle);// 主控制循环while (1) {key = getch();  // 无缓冲读取按键,无需回车// 退出程序if (key == 'q') {break;}buff[0] = 0xff;  buff[1] = 0x02;  buff[4] = 0xff;char status_msg[100] = {0};int send_flag = 0;// 处理按键逻辑switch (key) {case 'w':  // 红色臂+1°if (red_angle < 90) {red_angle++;buff[2] = 0x00;buff[3] = red_angle;sprintf(status_msg, "红色臂已更新至 %d° (发送成功)", red_angle);send_flag = 1;} else {sprintf(status_msg, "红色臂已达最大角度 90° (无法继续增加)");}break;case 's':  // 红色臂-1°if (red_angle > -90) {red_angle--;buff[2] = 0x00;buff[3] = red_angle;sprintf(status_msg, "红色臂已更新至 %d° (发送成功)", red_angle);send_flag = 1;} else {sprintf(status_msg, "红色臂已达最小角度 -90° (无法继续减小)");}break;case 'd':  // 蓝色臂+1°if (blue_angle < 180) {blue_angle++;buff[2] = 0x01;buff[3] = blue_angle;sprintf(status_msg, "蓝色臂已更新至 %d° (发送成功)", blue_angle);send_flag = 1;} else {sprintf(status_msg, "蓝色臂已达最大角度 180° (无法继续增加)");}break;case 'a':  // 蓝色臂-1°if (blue_angle > 0) {blue_angle--;buff[2] = 0x01;buff[3] = blue_angle;sprintf(status_msg, "蓝色臂已更新至 %d° (发送成功)", blue_angle);send_flag = 1;} else {sprintf(status_msg, "蓝色臂已达最小角度 0° (无法继续减小)");}break;default:sprintf(status_msg, "无效按键! 请使用 w/s/d/a/q");break;}// 发送数据if (send_flag) {int res = send(oldfd, buff, 5, 0);if (res == -1) {sprintf(status_msg, "发送失败: %s", strerror(errno));}}// 更新界面和显示状态show_info(red_angle, blue_angle);show_status(status_msg);}// 清理资源close(oldfd);endwin();  // 退出curses模式,恢复终端printf("程序已退出\n");return 0;
}

        单击wads键无需缓冲直接执行,长按wads多次执行,就实现了无极控制机械臂的摆动幅度,基本完成了项目需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94932.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习基础】机器学习的要素:任务T、性能度量P和经验E

第一章 机器学习的本质与理论框架 机器学习作为人工智能领域的核心支柱,其理论基础可以追溯到20世纪中叶的统计学习理论。Tom Mitchell在其1997年的经典著作《Machine Learning》中给出了一个至今仍被广泛引用的学习定义:"对于某类任务T和性能度量P,一个计算机程序被认…

wav音频转C语言样点数组

WAV to C Header Converter 将WAV音频文件转换为C语言头文件的Python脚本&#xff0c;支持将音频数据嵌入到C/C项目中。 功能特性 音频格式支持 PCM格式&#xff1a;支持8位、16位、24位、32位PCM音频IEEE Float格式&#xff1a;支持32位浮点音频多声道&#xff1a;支持单声道、…

01.《基础入门:了解网络的基本概念》

网络基础 文章目录网络基础网络通信核心原理网络通信定义信息传递过程关键术语解释网络的分类网络参考模型OSI 参考模型各层核心工作分层核心原则TCP/IP 参考模型&#xff08;4 层 / 5 层&#xff0c;实际应用模型&#xff09;TCP/IP 与 OSI 模型的对应关系传输层核心协议&…

基于vue驾校管理系统的设计与实现5hl93(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表&#xff1a;项目功能&#xff1a;学员,教练,教练信息,预约信息,场地信息,时间安排,车辆信息,预约练车,时间段,驾校场地信息,驾校车辆信息,预约报名开题报告内容&#xff1a;一、选题背景与意义背景随着汽车保有量持续增长&#xff0c;驾校行业规模不断扩大&am…

灰度思维:解锁世界原有本色的密码

摘要本文深入探讨灰度思维的概念内涵及其在处理他人评价中的应用价值。研究指出&#xff0c;灰度思维作为一种超越非黑即白的思维方式&#xff0c;能够帮助个体以更客观、全面的态度接受他人评价的片面性&#xff0c;从而促进个人成长和人际关系和谐。文章分析了他人评价片面性…

动态规划--Day03--打家劫舍--198. 打家劫舍,213. 打家劫舍 II,2320. 统计放置房子的方式数

动态规划–Day03–打家劫舍–198. 打家劫舍&#xff0c;213. 打家劫舍 II&#xff0c;2320. 统计放置房子的方式数 今天要训练的题目类型是&#xff1a;【打家劫舍】&#xff0c;题单来自灵艾山茶府。 掌握动态规划&#xff08;DP&#xff09;是没有捷径的&#xff0c;咱们唯一…

Nuxt.js@4 中管理 HTML <head> 标签

可以在 nuxt.config.ts 中配置全局的 HTML 标签&#xff0c;也可以在指定 index.vue 页面中配置指定的 HTML 标签。 在 nuxt.config.ts 中配置 HTML 标签 export default defineNuxtConfig({compatibilityDate: 2025-07-15,devtools: { enabled: true },app: {head: {charse…

UCIE Specification详解(十)

文章目录4.5.3.7 PHYRETRAIN&#xff08;物理层重训练&#xff09;4.5.3.7.1 Adapter initiated PHY retrain4.5.3.7.2 PHY initiated PHY retrain4.5.3.7.3 Remote Die requested PHY retrain4.5.3.8 TRAIN ERROR4.5.3.9 L1/L24.6 Runtime Recalibration4.7 Multi-module Link…

电商数据的获取方式:API、爬虫、第三方服务及更多

在竞争激烈的电商领域&#xff0c;数据是驱动业务增长的关键。准确、及时地获取电商数据&#xff0c;并进行深入分析&#xff0c;能够帮助企业洞察市场趋势、优化运营策略、提升用户体验。本文将全面介绍电商数据的获取方式&#xff0c;涵盖API接口、网络爬虫技术、第三方数据服…

《WINDOWS 环境下32位汇编语言程序设计》第8章 通用对话框

Windows操作系统为一些常用功能提供了一些通用对话框&#xff08;Common Dialog Box&#xff09;&#xff0c;比如&#xff0c;在不同的应用程序中进行打开文件、选择字体、选择颜色等操作时&#xff0c;不同程序显示的对话框的模样都是一样的。另外&#xff0c;把同样的应用程…

SOME/IP-SD协议中组播IP地址和端口号应从何处获取、由谁设置?

<摘要> AUTOSAR SOME/IP-SD协议中组播通信参数的核心配置规则明确规定了在服务端传输&#xff08;Server-Transmits&#xff09;和客户端传输&#xff08;Client-Transmits&#xff09;两种模式下&#xff0c;组播IP地址和端口号应从何处获取、由谁设置&#xff0c;从而确…

DAY49打卡

追到第45天内容浙大疏锦行

十四、测试 (Testing)

Rust内置了强大的测试框架,使得编写和运行测试变得非常简单。Rust的测试系统主要包括单元测试、集成测试和文档测试。 1. 单元测试 单元测试通常放在与被测试代码相同的文件中,使用#[cfg(test)]模块和#[test]属性标记。 1.1 基本测试结构 // 在src/lib.rs或任何模块中pub…

LeetCode 刷题【56. 合并区间】

56. 合并区间 自己做 解&#xff1a;排序合并 class Solution { public:static bool compare(const vector<int> &p1, const vector<int> &p2){ //按第一个数排序return p1[0] < p2[0]; }vector<vector<int>> merge(ve…

DistributedLock 实现.Net分布式锁

在分布式系统中&#xff0c;经常会遇到多个实例同时访问同一份资源的情况&#xff0c;例如&#xff1a; • 多个服务节点同时写入数据库同一行数据• 定时任务在多个节点上同时运行&#xff0c;导致重复执行• 多实例写缓存时出现数据覆盖问题 为了解决 并发冲突 和 数据一致…

Flutter:ios打包ipa,证书申请,Xcode打包,完整流程

步骤1 - 5 为 申请ios的签名文件&#xff0c;App ID&#xff0c;证书&#xff0c;描述文件&#xff0c;并添加测试打包设备。 步骤1&#xff1a;生成证书签名文件&#xff08;打开钥匙串访问>证书助理>从证书颁发机构请求证书&#xff09; 存储后得到了一个签名文件&…

Shell 秘典(卷二)——号令延展秘术 与 流程掌控心法・if 天机判语篇精解

文章目录前言一、命令扩展详解1.1 逻辑运算符1.1.1 逻辑与运算符&#xff08;&&&#xff09;1.1.2 逻辑或运算符&#xff08;||&#xff09;1.1.3 组合使用注意事项1.2 echo 命令1.2.1 基本用法1.2.2 输出到标准错误&#xff08;stderr&#xff09;1.3 标准文件描述符&…

Agent实战教程:深度解析async异步编程在Langgraph中的性能优化

在现代Python开发中&#xff0c;异步编程已经成为提高程序性能的重要手段&#xff0c;特别是在处理网络请求、数据库操作或AI模型调用等耗时操作时。本文将通过实际的LangGraph 示例&#xff0c;深入解析async的真正作用&#xff0c;并揭示一个常见误区&#xff1a;为什么异步顺…

coalesce在sql中什么作用

COALESCE‌是SQL中的一个函数&#xff0c;用于返回参数列表中的第一个非空值&#xff0c;若所有参数均为NULL则返回NULL&#xff0c;常用于处理数据中的空值情况。 ‌核心功能与语法‌ COALESCE函数的基本语法为&#xff1a;COALESCE(expression1, expression2, ..., express…

【Rust】 6. 字符串学习笔记

一、Rust 字符串概述 Rust 字符串是 UTF-8 编码的文本序列&#xff0c;提供两种主要类型&#xff1a; &str - 字符串切片&#xff08;通常作为引用出现&#xff09;String - 动态可变的、拥有所有权的字符串 二、字符串字面量 (&str) 编译时已知大小&#xff0c;静态分…