【逆运动学】六轴机器人运动学逆解上位机控制

六轴机器人逆运动学上位机控制

最终效果

通过开发的上位机软件,实现对机械臂末端的精准操控。该软件接收输入的坐标与角度参数,经过算法处理计算出机械臂各关节轴的目标角度。随后,这些角度值被转换为对应的脉冲宽度调制(PWM)信号数值并传输至下位机,进而驱动舵机偏转至指定位置。

Robot_Arm


在这里插入图片描述

准备

机器人下位机已经写好信号接收和舵机控制的程序;

电脑上安装好Visual Studio 和 CH341串口驱动;

软件编写

编写流程

  1. 打开 Visual Studio 。
  2. 在起始页中,选择“创建新项目”。
  3. 在“创建新项目”窗口中,选择“Windows 桌面”类别。
  4. 在右侧的模板列表中,选择“Windows 窗体应用(.NET Framework)”或“控制台应用程序”等您想要创建的应用程序类型。
  5. 为项目指定一个名称和存储位置,然后点击“确定”。
  6. 根据选择的应用程序类型,Visual Studio 会创建基本的项目结构和代码框架。
  7. 然后就可以在相应的代码文件(例如 Form1.cs 或 Program.cs)中编写 C# 代码来实现应用程序逻辑。

软件界面部分代码

private void UpdateSendDataTextBox()
{
    double x_input = trackBar1.Value - x_origin;
    double y_input = trackBar2.Value;
    double z_input = trackBar3.Value;
    alpha = trackBar4.Value;


    label_Xmin.Text = $"-{x_origin}";
    label_Xmax.Text = $"{x_origin}";
    label_Ymax.Text = $"{z_origin}";
    label_Zmax.Text = $"{z_origin}";

    textBox_alpha.Text = trackBar4.Value.ToString();
    textBox_Z.Text = trackBar3.Value.ToString();
    textBox_Y.Text = trackBar2.Value.ToString();
    textBox_X.Text = (trackBar1.Value - x_origin).ToString();
    textBox_wrist.Text = theta5.ToString();
    textBox_hand.Text = theta6.ToString();

    if (JiSuan(x_input, y_input,z_input, alpha))
    {
        textBoxSendData.Text =
        $"\r\n 舵机6: {theta1:F2}°" +
        $"\r\n 舵机5: {theta2:F2}°" +
        $"\r\n 舵机4: {theta3:F2}°" +
        $"\r\n 舵机3: {theta4:F2}°" +
        $"\r\n 舵机2: {theta5:F2}°" +
        $"\r\n 舵机1: {theta6:F2}°" +
        $"\r\n alpha: {alpha:F2}°" +
        $"\r\n 速度:  {Value_speed}ms";
        Solvable_flage = true;
    }
    else
    {
        textBoxSendData.Text = 
        $"\r\n 舵机6: 无解" +
        $"\r\n 舵机5: 无解" +
        $"\r\n 舵机4: 无解" +
        $"\r\n 舵机3: 无解" +
        $"\r\n 舵机2: {theta5:F2}°" +
        $"\r\n 舵机1: {theta6:F2}°" +
        $"\r\n alpha: {alpha:F2}°" +
        $"\r\n 速度:  {Value_speed}ms";
        Solvable_flage = false;
    }
}
/*复位按钮*/
private void buttonPlus1_Click(object sender, EventArgs e)
{
    trackBar1.Value = x_origin;
    trackBar2.Value = y_origin;
    trackBar3.Value = z_origin;
    trackBar4.Value = 90;
    Value_speed = 1000;
    theta5 = wrist_origin;
    theta6 = hand_origin;
    textBox_speed.Text = $"{Value_speed}";

    UpdateSendDataTextBox();
    SendData();
}
//=滑动条及输入框部分================================================================================================

private void textBox_wrist_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_wrist.Text, out int value))
        {
            //textBox_X.Text = trackBar1.Value.ToString();
            if (value >= 0 && value <= 180)
            {
                theta5 = value;
            }
            else
            {
                MessageBox.Show($"请输入范围在 0 到 180 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}

private void textBox_hand_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_hand.Text, out int value))
        {
            //textBox_X.Text = trackBar1.Value.ToString();
            if (value >= 0 && value <= 180)
            {
                theta6 = value;
            }
            else
            {
                MessageBox.Show($"请输入范围在 0 到 180 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}
private void textBox_speed_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_speed.Text, out int value))
        {
            //textBox_X.Text = trackBar1.Value.ToString();
            if (value >= 100 && value <= 3000)
            {
                Value_speed = value;
            }
            else
            {
                MessageBox.Show($"请输入范围在 100 到 3000 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
}
//-----滑动条-文本框alpha-----------------------------------------------------------------
/*输入框alpha*/
private void textBox_alpha_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_alpha.Text, out int value))
        {
            textBox_alpha.Text = trackBar4.Value.ToString();
            if (value >= trackBar4.Minimum && value <= trackBar4.Maximum)
            {
                trackBar4.Value = value;
            }
            else
            {
                MessageBox.Show($"请输入范围在 {trackBar4.Minimum} 到 {trackBar4.Maximum} 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}
private void trackBar4_ValueChanged(object sender, EventArgs e)
{
    UpdateSendDataTextBox();
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}
//-------滑动条-文本框Z-----------------------------------------------------------
private void trackBar3_Scroll(object sender, EventArgs e)
{
    UpdateSendDataTextBox();
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}
/*输入框Z*/
private void textBox_Z_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_Z.Text, out int value))
        {
            textBox_Z.Text = trackBar3.Value.ToString();
            if (value >= trackBar3.Minimum && value <= trackBar3.Maximum)
            {
                trackBar3.Value = value;
            }
            else
            {
                MessageBox.Show($"请输入范围在 {trackBar3.Minimum} 到 {trackBar3.Maximum} 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
}
//-------------------------------------------------------------------
/*检查滑动条trackBar2*/
private void trackBar2_Scroll(object sender, EventArgs e)
{
    UpdateSendDataTextBox();
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}
/*输入框Y*/
private void textBox_Y_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_Y.Text, out int value))
        {
            textBox_Y.Text = trackBar2.Value.ToString();
            if (value >= trackBar2.Minimum && value <= trackBar2.Maximum)
            {
                trackBar2.Value = value;
            }
            else
            {
                MessageBox.Show($"请输入范围在 {trackBar2.Minimum} 到 {trackBar2.Maximum} 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
}
//---------------------------------------------------------------
/*检查滑动条trackBar1*/
private void trackBar1_Scroll(object sender, EventArgs e)
{
    UpdateSendDataTextBox();
    if (isRealtimeSendEnabled)
    {
        SendData();
    }
}
/*输入框X*/
private void textBox_X_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        if (int.TryParse(textBox_X.Text, out int value))
        {
            textBox_X.Text = trackBar1.Value.ToString();
            if (value >= trackBar1.Minimum-x_origin && value <= trackBar1.Maximum-x_origin)
            {
                trackBar1.Value = value+x_origin;
            }
            else
            {
                MessageBox.Show($"请输入范围在 {trackBar1.Minimum} 到 {trackBar1.Maximum} 之间的整数!");
            }
        }
        else
        {
            MessageBox.Show("请输入一个有效的整数!");
        }
        UpdateSendDataTextBox();
    }
}

串口通信协议

在这里插入图片描述

在这里插入图片描述

根据上面的通信协议内容,便可以基本明确串口应该如何发送数据。

示例:

下图是控制六个舵机,每个舵机的角度值为1500 (0x5DC),移动速度为1111 (0x457)时的数据格式

在这里插入图片描述

为什么要分成高八位和低八位:

  • 单字节传输机制:串口通信中,数据是以字节为单位进行传输的。每个字节包含8位,这是由串口的硬件结构决定的。

  • 连续传输需求:当需要传输的数据超过一个字节,即大于8位时,必须将数据分割成多个字节进行连续传输。

  • 缓存限制:串口的收发寄存器SBUF是八位的,这意味着一次只能处理8位数据。如果赋予SBUF超过8位的数据,它只能取低八位。

    所以,当传输的数据大于八位,即大于255时,就需要将其分成高八位和低八位。

串口数据格式代码

/*转换为数据帧格式*/
private void UnifcatData() 
{
    // t1角度 - 6舵机
    // t2角度 - 5舵机
    // t3角度 - 4舵机
    // t4角度 - 3舵机
    // t5角度 - 2舵机
    // t6角度 - 1舵机
    //舵机pwm值范围为500~2500,对应角度0~180
    PWMval1 = (int)(theta6 * 11.11) + 500;
    PWMval2 = (int)(theta5 * 11.11) + 500;
    PWMval3 = (int)(theta4 * 11.11) + 500;
    PWMval4 = (int)(theta3 * 11.11) + 500;
    PWMval5 = (int)(theta2 * 11.11) + 500;
    PWMval6 = (int)(theta1 * 11.11) + 500;

    bytesToSend[0] = 85;//0x55
    bytesToSend[1] = 85;//0x55 帧头
    bytesToSend[2] = 23;//0x17 长度
    bytesToSend[3] = 3;//0x03 指令
    bytesToSend[4] = 6;//0x06 控制舵机个数
    bytesToSend[5] = (byte)Value_speed;//移动时间 Value_speed
    bytesToSend[6] = (byte)(Value_speed >> 8);//0x04 
    bytesToSend[7] = 1;//0x01 id1
    bytesToSend[8] = (byte)PWMval1;//0x04 
    bytesToSend[9] = (byte)(PWMval1 >> 8);//0x04
    bytesToSend[10] = 2;//0x02 id2
    bytesToSend[11] = (byte)PWMval2;//0x04
    bytesToSend[12] = (byte)(PWMval2 >> 8);//0x04
    bytesToSend[13] = 3;//0x03 id3
    bytesToSend[14] = (byte)PWMval3;//0x04
    bytesToSend[15] = (byte)(PWMval3 >> 8);//0x04
    bytesToSend[16] = 4;//0x04 id4
    bytesToSend[17] = (byte)PWMval4;//0x04
    bytesToSend[18] = (byte)(PWMval4 >> 8);//0x04
    bytesToSend[19] = 5;//0x05 id5
    bytesToSend[20] = (byte)PWMval5;//0x04
    bytesToSend[21] = (byte)(PWMval5 >> 8);//0x04
    bytesToSend[22] = 6;//0x06 id6
    bytesToSend[23] = (byte)PWMval6;//0x04
    bytesToSend[24] = (byte)(PWMval6 >> 8);//0x04
}

串口连接代码

private void InitializeComboBox()
{
    // 常用波特率
    int[] baudRates = { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200 };

    // 将波特率添加到ComboBox中
    object[] baudRateObjects = Array.ConvertAll(baudRates, item => (object)item);
    comboBoxBaudRate.Items.AddRange(baudRateObjects);

    // 设置默认波特率为9600
    comboBoxBaudRate.SelectedItem = 9600;

    // 注册事件处理器
    comboBoxBaudRate.SelectedIndexChanged += ComboBoxBaudRate_SelectedIndexChanged;
}
private void comboBoxBaudRate_SelectedIndexChanged(object sender, EventArgs e)
{
    // 当ComboBox的选定值改变时,更新bot_val变量
    bot_val = (int)comboBoxBaudRate.SelectedItem;
    //MessageBox.Show($"波特率已更改为: {bot_val}");
}
/*波特率选项框*/
private void ComboBoxBaudRate_SelectedIndexChanged(object sender, EventArgs e)
{
    throw new NotImplementedException();
}

/*串口搜索框*/
private void GetAvailablePorts()
{
    comboBoxPorts.Items.Clear();

    var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Caption LIKE '%(COM%)'");
    var portNames = SerialPort.GetPortNames();
    var ports = new List<string>();

    foreach (ManagementObject queryObj in searcher.Get())
    {
        string name = queryObj["Caption"].ToString();
        foreach (string port in portNames)
        {
            if (name.Contains(port))
            {
                ports.Add(name);
                break;
            }
        }
    }

    comboBoxPorts.Items.AddRange(ports.ToArray());
}

/*串口刷新按钮*/
private void buttonRefresh_Click(object sender, EventArgs e)
{
    GetAvailablePorts();
}

/*串口发送按钮*/
private void buttonSend_Click(object sender, EventArgs e)
{
    SendData();
}

private void Form1_Load(object sender, EventArgs e)
{

}

/*检查串口搜索框,连接串口*/
private void comboBoxPorts_SelectedIndexChanged(object sender, EventArgs e)
{
    if (serialPort != null && serialPort.IsOpen)
    {
        serialPort.Close();
    }

    string selectedPortInfo = comboBoxPorts.SelectedItem.ToString();
    string selectedPort = selectedPortInfo.Substring(selectedPortInfo.LastIndexOf("(COM")).Replace("(", "").Replace(")", "");

    serialPort = new SerialPort(selectedPort)
    {
        BaudRate = bot_val, // 设置波特率
        ReadTimeout = 3000, // 读取超时设置为3秒
        WriteTimeout = 3000 // 写入超时设置为3秒
    };
    //serialPort.DataReceived += SerialPort_DataReceived;

    try
    {
        serialPort.Open();
    }
    catch (UnauthorizedAccessException ex)
    {
        MessageBox.Show("访问串口被拒绝: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    catch (IOException ex)
    {
        MessageBox.Show("串口打开失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    catch (ArgumentException ex)
    {
        MessageBox.Show("无效的串口参数: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    catch (InvalidOperationException ex)
    {
        MessageBox.Show("串口状态无效: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
/*检查是否勾选实时发送*/
private void checkBoxRealtimeSend_CheckedChanged(object sender, EventArgs e)
{
    isRealtimeSendEnabled = checkBoxRealtimeSend.Checked;
}

/*向串口发送数据*/
private void SendData()
{
    if (serialPort != null && serialPort.IsOpen)
    {
        if (Solvable_flage)
        {
            try
            {
                UnifcatData();
                serialPort.Write(bytesToSend, 0, bytesToSend.Length);
                //System.Threading.Thread.Sleep(1000);// 等待数据发送完成
            }
            catch (Exception ex)
            {
                MessageBox.Show("发送数据时出错: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        else
        {
            if (!isRealtimeSendEnabled)
                MessageBox.Show("无解,请调整坐标 " , "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }    
    }
    else
    {
        MessageBox.Show("请选择一个串口并确保它是打开的", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

运动学逆解

在这里插入图片描述

计算步骤

首先,测量出机械臂的长度(mm):

  • L 1 = 105.0 L_1 = 105.0 L1=105.0
  • L 2 = 89.0 L_2 = 89.0 L2=89.0
  • L 3 = 180.0 L_3 = 180.0 L3=180.0
  • 计算在xy平面内的底盘舵机角度 θ 1 \theta_1 θ1
    • θ 1 = arctan ⁡ ( y x ) \theta_1 = \arctan(\frac{y}{x}) θ1=arctan(xy)

将三维立体坐标转换到二维平面内

  • 机械臂末端的高度和水平距离 :

    • D = ∣ x 2 + y 2 ∣ D = \sqrt{|x^2 + y^2|} D=x2+y2 H = z H = z H=z
  • L 3 L_3 L3 x x x 方向和 y y y 方向的分量:

    • x α = L 3 × cos ⁡ ( α r a d ) x_{\alpha} = L_3 \times \cos(\alpha_{rad}) xα=L3×cos(αrad) y α = L 3 × sin ⁡ ( α r a d ) y_{\alpha} = L_3 \times \sin(\alpha_{rad}) yα=L3×sin(αrad)
  • 连杆 L 1 L_1 L1 L 2 L_2 L2 x x x y y y 分量:

    • x θ = D − x α x_{\theta} = D - x_{\alpha} xθ=Dxα y θ = H − y α y_{\theta} = H - y_{\alpha} yθ=Hyα
  • 连杆 L 2 L_2 L2末端到原点的距离:

    • d θ = x θ 2 + y θ 2 d_{\theta} = \sqrt{x_{\theta}^2 + y_{\theta}^2} dθ=xθ2+yθ2
  • 连杆 L 1 L_1 L1 L 2 L_2 L2之间的夹角 Θ \Theta Θ

    • Θ = arccos ⁡ ( L 1 2 + L 2 2 − d θ 2 2 × L 1 × L 2 ) \Theta = \arccos(\frac{L_1^2 + L_2^2 - d_{\theta}^2}{2 \times L_1 \times L_2}) Θ=arccos(2×L1×L2L12+L22dθ2)
  • 舵机 4 的角度 θ 3 \theta_3 θ3

    • θ 3 = 180 − π × 1.5 − Θ 180 \theta_3 = 180 - \frac{\pi \times 1.5 - \Theta}{180} θ3=180180π×1.5Θ​​ (转换为角度)
  • 角度 θ 2 1 \theta_{2_1} θ21 θ 2 2 \theta_{2_2} θ22

    • θ 2 1 = arccos ⁡ ( L 1 2 + d θ 2 − L 2 2 2 × L 1 × d θ ) \theta_{2_1} = \arccos(\frac{L_1^2 + d_{\theta}^2 - L_2^2}{2 \times L_1 \times d_{\theta}}) θ21=arccos(2×L1×dθL12+dθ2L22) θ 2 2 = arccos ⁡ ( x θ d θ ) \theta_{2_2} = \arccos(\frac{x_{\theta}}{d_{\theta}}) θ22=arccos(dθxθ)
  • 舵机 5 的角度 θ 2 \theta_2 θ2

    • θ 2 = 180 − ( θ 2 1 180 + θ 2 2 180 ) \theta_2 = 180 - (\frac{\theta_{2_1}}{180} + \frac{\theta_{2_2}}{180}) θ2=180(180θ21+180θ22)
  • 舵机 3 的角度 θ 4 \theta_4 θ4

    • θ 4 = ∣ Θ − α r a d + θ 2 1 + θ 2 2 180 − 90 ∣ \theta_4 = |\frac{\Theta - \alpha_{rad} + \theta_{2_1} + \theta_{2_2}}{180} - 90| θ4=180Θαrad+θ21+θ2290∣

逆解代码

private const double PI = Math.PI;
private const double L1 = 105.0;  // 机械臂长度
private const double L2 = 89.0;  // 机械臂长度
private const double L3 = 180.0;  // 机械臂长度 
/*计算底盘舵机角度,并将坐标转换到二维*/
public static bool JiSuan(double x, double y, double z, double alpha)
{
    //计算底盘舵机角度
    theta1 = Rad2Deg(Math.Atan2(y, x));
    double H = z; // 高度
    double D = Math.Sqrt(Math.Abs(x * x) + y * y); // 水平距离

    return (My_Model(D, H, alpha));
}

//在二维平面计算角度
public static bool My_Model(double D, double H, double alpha)
{
    alpha = Deg2Rad(alpha);//转为弧度
    double x_alpha = L3 * Math.Cos(alpha);
    double y_alpha = L3 * Math.Sin(alpha);

    double x_theta = D - x_alpha;
    double y_theta = H - y_alpha;
    double d_theta = Math.Sqrt(x_theta* x_theta+ y_theta* y_theta);

    double Theta = Math.Acos((L1*L1+L2*L2-d_theta*d_theta)/(L1*L2*2));

    theta3 = Rad2Deg(PI*1.5 - Theta); // 舵机4
    double theta2_1 = Math.Acos((L1*L1+d_theta*d_theta-L2*L2)/(L1*d_theta*2));
    double theta2_2 = Math.Acos(x_theta/d_theta);
    theta2 =180 - Rad2Deg(theta2_1+theta2_2); // 舵机5

    theta4 = Math.Abs(Rad2Deg(Theta- alpha + theta2_1 + theta2_2) - 90);// 舵机3

    return IsSolutionValid(theta1, theta2, theta3, theta4);
}

/*检查解是否在有效范围内*/
static bool IsSolutionValid(double theta1, double theta2, double theta3, double theta4)
{
    return (theta1 >= 0 && theta1 <= 180 &&
            theta2 >= 0 && theta2 <= 180 &&
            theta3 >= 0 && theta3 <= 180 &&
            theta4 >= 0 && theta4 <= 180);
}
/*弧度转换为度*/
static double Rad2Deg(double rad)
{
    return rad * 180 / Math.PI;
}
/*角度转换为弧度*/
static double Deg2Rad(double rad)
{
    return rad * Math.PI / 180;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

建筑物健康监测:振弦式应变计的选型与应用

在现代建筑工程中&#xff0c;建筑物健康监测(Structural Health Monitoring, SHM)越来越受到关注。通过实时监测建筑结构的状态&#xff0c;确保建筑物的安全和耐久性&#xff0c;是工程师和建筑师们面临的重要任务。振弦式应变计作为一种关键的监测工具&#xff0c;因其高精…

uni-app 自定义支付密码键盘

1.新建最贱 payKeyboard .vue <template><view class"page-total" v-show"isShow"><view class"key-list"><view class"list" v-for"(item,index) in keyList" :class"{special:item.keyCode190…

前端存储都有哪些

cookie 、sessionStorage、localStorange、http缓存 、indexDB cookie 由服务器设置&#xff0c;在客户端存储&#xff0c;然后每次发起同源请求时&#xff0c;发送给服务器端。cookie最多能存储4K数据&#xff0c;它的生存时间由expires属性指定&#xff0c;并且cookie只能被…

粮油包装生产线:智能化升级助力产业腾飞

随着科技的不断进步和产业的深入发展&#xff0c;粮油包装生产线在智能化和产业升级中扮演着越来越关键的角色。智能化不仅提升了生产线的效率和精度&#xff0c;还为实现产业升级、提升产品质量、降低成本等方面带来了革命性的变化。 一、智能化改造&#xff0c;效率大提升 传…

微信小程序开发 小白入门篇

小程序内容简介 能够知道如何创建小程序项目 微信开发者工具的使用、appID 的获取能够清楚小程序项目的基本组成结构 app.js、app.json、app.wxss、pages 文件夹能够知道小程序页面由几部分组成 wxml、wxss、json、js能够知道小程序中常见的组件如何使用 view、text、image能够…

以指标为中心,Aloudata 重塑企业数据生产力

6 月 25 日 - 6 月 26 日&#xff0c;第十届 CDIE 数字化创新博览会在上海张江科学会堂隆重举行。作为国内领先的自动化数据管理软件提供商&#xff0c;Aloudata 大应科技携自主研发的 Aloudata CAN 自动化指标平台亮相&#xff0c;全面展现了其独创的“NoETL”架构理念&#x…

vue-cil搭建项目

目录 一、使用 HbuilderX 快速搭建一个 vue-cli 项目 1.需要的环境——Node.js 2.搭建Vue-cil项目 二、组件路由 1.安装vue-router 2.创建router目录 3.使用路由 4.在main.js中配置路由 vue-cli 官方提供的一个脚手架&#xff0c;用于快速生成一个 vue 的项目模板&#xff1b;…

【机器学习】Whisper:开源语音转文本(speech-to-text)大模型实战

目录 一、引言 二、Whisper 模型原理 2.1 模型架构 2.2 语音处理 2.3 文本处理 三、Whisper 模型实战 3.1 环境安装 3.2 模型下载 3.3 模型推理 3.4 完整代码 3.5 模型部署 四、总结 一、引言 上一篇对​​​​​​​ChatTTS文本转语音模型原理和实战进行了讲解&a…

Charles网络抓包工具安装和web抓包(一)

目录 概述 抓包工具对比 安装 下载 web抓包配置 按键说明 前言-与正文无关 ​ 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的漩涡&#xff0c;忘记了停下脚步&#…

Kubernetes Prometheus 系例 | kubernetes 部署 Kafka exporter监控Kafka集群

prometheus 监控 kafka 常见的有两种开源方案&#xff1b; 部署 exporter 或 jmx 配置监控。 项目地址&#xff1a; kafka_exporter&#xff1a;https://github.com/danielqsj/kafka_exporter jmx_exporter&#xff1a;https://github.com/prometheus/jmx_exporter 本文采用kaf…

布尔运算00

题目链接 布尔运算 题目描述 注意点 运算符的数量不超过 19 个布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成算出有几种可使该表达式得出 result 值的括号方法 解答思路 可以使用动态规划根据左右两侧区间不同结果相应组合数量计算得出当前…

WINDOWS+PHP+Mysql+Apache环境中部署SQLi-Labs、XSS-Labs、UPload-Labs、DVWA、pikachu等靶场环境

web渗透测试学习&#xff0c;需要自己搭建一些靶场&#xff0c;本人主要介绍在WINDOWSPHPMysqlApache环境中部署SQLi-Labs、XSS-Labs、UPload-Labs、DVWA、pikachu等靶场环境。以下是靶场代码下载的链接&#xff1a; pikachu靶场代码 链接&#xff1a;https://pan.baidu.com/s…

C++——string类用法指南

一、前言 在C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离的&#xff0c;不太符合OOP的思想&#xff0c;而且底层空间需要用户自己管理&#xff0c;稍…

RAGOnMedicalKG:大模型结合知识图谱的RAG实现

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径&#xff1a;AI代理工作流大模型应用开发实用开源项目汇总大模…

数据可视化如何为智慧农业带来变革

数据可视化如何为智慧农业保驾护航&#xff1f;随着农业现代化的深入推进&#xff0c;智慧农业应运而生&#xff0c;通过集成物联网、大数据、人工智能等先进技术&#xff0c;实现农业生产的数字化、智能化和高效化。而在这一过程中&#xff0c;数据可视化技术作为重要的工具&a…

文本分析|小白教程

在信息爆炸的时代&#xff0c;文本数据无处不在&#xff0c;如何从这些海量的文字中提炼出有价值的信息呢&#xff1f;答案就是——文本分析。文本分析&#xff0c;简单来说&#xff0c;就是对文本数据进行深度的研究和分析。它能够从看似普通的文字中&#xff0c;提取出主题、…

Git之checkout/reset --hard/clean -f区别(四十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

第6章_libmodbus使用

文章目录 第6章 libmodbus使用6.1 libmodbus开发库6.1.1 功能概要6.1.2 源码获取6.1.3 源码阅读1. 新建工程2. 同步文件3.打开工程4. 操作示例5. 快捷键 6.1.4 libmodbus与应用程序的关系 6.2 libmodbus源代码解析6.2.1 核心函数6.2.2 框架分析与数据结构6.2.3 情景分析1. 初始…

Springboot 整合 DolphinScheduler(一):初识海豚调度

目录 一、什么是 DolphinScheduler 二、DolphinScheduler 的特性 三、DolphinScheduler 核心架构 四、单机环境部署流程 1、下载安装包 2、上传至服务器&#xff0c;解压缩 3、单机启动 4、登录 dolphinscheduler UI 5、配置数据库【非必需】 &#xff08;1&#xff…

新风口不再是直播,云微客带你领略短视频矩阵的魅力

只要你细心观察&#xff0c;就能发现很多品牌都在做短视频矩阵&#xff0c;正是凭借大量的短视频矩阵账号带来的流量曝光&#xff0c;这些品牌才能覆盖数以万计的客户人群&#xff0c;才能每天不断地产生新订单。 有很多人觉得矩阵不就是多注册账号吗&#xff1f;其实短视频矩阵…