首页 > 教程攻略 > ai资讯 >AI大模型落地应用之电机驱动

AI大模型落地应用之电机驱动

来源:互联网 时间:2026-08-12 15:58:09

AI大模型落地应用之电机驱动

AI大模型落地应用之电机驱动

一、关于将AI大模型落地的一点思考

2023年,ChatGPT的横空出世让大模型进入高速发展期,这一年也被业界称为“人工智能元年”。到了2024年,大模型技术逐渐成熟,开始试图迈入下一个阶段——大模型落地阶段。但问题来了:到底该如何落地?怎么落地?

说到底,AI虽然能赋能各行各业,但从某种意义上讲,它更像一个虚拟产品。要让它真正落地,就得跟实体产业结合起来。而在众多实体产业中,机器人无疑是最契合的一个选择。

二、如何使用大模型技术结合机器人技术?

相比硬件部分,机器人控制部分的软件应该是最适合与AI大模型结合的。这里以简单的AI驱动电机作为例子来说明整个流程。

1. 准备工作

首先需要获取API密钥:OpenAI API官网链接:https://platform.openai.com/docs/api-reference

安装包含Python环境的Anaconda,相比直接安装Python,它能隔离多个环境,给每个项目创建纯净的环境,避免包之间的冲突。

Python
环境安装
安装anaconda
https://www.anaconda.com/products/individual
打开命令行,创建虚拟环境
conda create -n openai_control python=3.11
下载需要的包
pip install openai

这里使用的是PyCharm,所以有些终端操作可以直接略过。

2. 代码编写

关于电机驱动控制的操作,因为只是介绍AI落地应用的方向,所以没有搞得太复杂。核心思路就是:大模型 + Tool + 串口通信 + STM32控制。

首先构造出ChatGPT对话的基本框架:

Python
import openai

api_key = open('api_key.txt', 'r').read().strip()
# 创建OpenAI的client,需要设置api_key
client = openai.OpenAI(api_key=api_key)

# 调用chat接口
chat_completion = client.chat.completions.create(
# 发送的消息内容,类似在ChatGPT中输入"hi,你好,请介绍下自己"
messages=[{
"role": "user",
"content": "hi,你好,请介绍下自己.",
}],
# gpt模型选择
model='gpt-3.5-turbo-1106',
# 非流式输出
stream=False
)

# GPT返回的答复
print(chat_completion.choices[0].message.content)

3. 构造电机驱动的Tools

Tools的本质就是插件,相当于给OpenAI增加了一个额外的扩展,开发者通过这个扩展提升它的能力。调用流程如下:

  • 定义本地函数Motor_control,实现从API拉取(这里直接写一个简单的参数输出模拟)。
  • 第一次调用接口:返回大模型分析出的函数名称和参数。
  • 调用本地函数:获取返回值,进行本地Python方法调用。
  • 第二次调用接口:将第一次接口的返回值message与本地函数调用的结果拼装起来,再次调用接口。

具体代码:

Python
import openai

# 自定义一个插件方法,用于进行电机的启动控制
def Motor_control():
return "启动"

# 定义messages
messages = [{
"role": "user",
"content": "hi,你好,你能启动一下电机吗?",
}]

api_key = open('api_key.txt', 'r').read().strip()
client = openai.OpenAI(api_key=api_key)

first_chat_completion = client.chat.completions.create(
messages=messages,
model='gpt-3.5-turbo-1106',
stream=False,
tool_choice="auto",
tools=[{
"type": "function",
"function": {
"name": "Motor_control",
"description": "控制电机的启动和停止",
}
}]
)

response_message = first_chat_completion.choices[0].message
print(response_message)
messages.append(response_message)

if response_message.tool_calls is not None:
print("命中插件,进行调用")
messages.append({
"role": "tool",
"content": Motor_control(),
"tool_call_id": response_message.tool_calls[0].id
})

second_chat_completion = client.chat.completions.create(
messages=messages,
model='gpt-3.5-turbo-1106',
stream=False,
tool_choice="auto",
tools=[{
"type": "function",
"function": {
"name": "Motor_control",
"description": "控制电机的启动和停止",
}
}]
)
print(second_chat_completion.choices[0].message.content)

逻辑上看起来没问题,也命中了插件,但这里报错了:

Python
raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not ha ve response messages: call_PEhO1CpC6DcW3QKQFacJqO2b", 'type': 'invalid_request_error', 'param': 'messages', 'code': None}}

经过排查,发现是tool_call_id的问题。如果确定只有单个tool_call_id需要响应,代码应该只关注处理这一个响应,确保在向messages列表追加响应时正确引用tool_call_id并提供实际响应内容。但这里的代码似乎不止一次引用。于是进行修正:

Python
import openai

def Motor_control():
return "电机已启动"

messages = [{
"role": "user",
"content": "hi,你好,你能启动一下电机吗?",
}]

api_key = open('api_key.txt', 'r').read().strip()
client = openai.OpenAI(api_key=api_key)

first_chat_completion = client.chat.completions.create(
messages=messages,
model='gpt-3.5-turbo-1106',
stream=False,
tool_choice="auto",
tools=[{
"type": "function",
"function": {
"name": "Motor_control",
"description": "控制电机的启动和停止",
}
}]
)

response_message = first_chat_completion.choices[0].message
print(response_message)
messages.append(response_message)

if response_message.tool_calls is not None:
print("命中插件,进行调用")
for tool_call in response_message.tool_calls:
tool_response_content = Motor_control()
messages.append({
"role": "tool",
"content": tool_response_content,
"tool_call_id": tool_call.id
})

second_chat_completion = client.chat.completions.create(
messages=messages,
model='gpt-3.5-turbo-1106',
stream=False,
tool_choice="auto",
tools=[{
"type": "function",
"function": {
"name": "Motor_control",
"description": "控制电机的启动和停止",
}
}]
)
print(second_chat_completion.choices[0].message.content)

这次成功调用。

4. 串口通信 + STM32控制

在基础的插件命中之后,就可以进行串口通信+STM32的控制操作了。因为本次只是为了电机的启动和停止,只需要对原本的Motor_control函数进行修改即可。当然,还需要下载pyserial的包。修改后的Motor_control函数:

Python
import serial

ser = serial.Serial('COM3', 9600)

def Motor_control(user_input):
"""
根据用户输入决定发送启动(1)或停止(0)命令到串口。
:param user_input: 用户的原始输入文本
:return: 成功执行的响应信息
"""
if "启动" in user_input or "开" in user_input:
command = "1"
ser.write(command.encode())
return "电机已启动"
elif "停止" in user_input or "关" in user_input:
command = "0"
ser.write(command.encode())
return "电机已停止"

由于涉及到参数,还需要对GPT回复部分的插件参数进行重新注册:

Python
def interact_with_gpt():
completion = client.chat.completions.create(
messages=messages,
model='gpt-3.5-turbo-1106',
stream=False,
tool_choice="auto",
tools=[
{
"type": "function",
"function": {
"name": "Motor_control",
"description": "控制电机的启动和停止",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "启动或停止电机的命令",
}
},
"required": ["command"]
},
}
},
]
)
return completion

再次报错,原因是tool_call.function.arguments是一个字符串而不是字典,需要用json.loads转换:

Python
if response.tool_calls is not None:
print("命中插件,进行调用")
if response.tool_calls:
for tool_call in response.tool_calls:
arguments = json.loads(tool_call.function.arguments)
if tool_call.function.name == "Motor_control":
tool_response = Motor_control(arguments.get("command", ""))
else:
tool_response = {"content": "未知的工具调用。"}
messages.append({
"role": "tool",
"content": json.dumps(tool_response),
"tool_call_id": tool_call.id
})

至此,电机启动!但有个问题:我们的目的是通过串口控制电机,得知道到底发送了什么串口数据。GPT只告诉电机启动成功,没有告诉发送了什么数据。于是再次修改电机控制函数,明确告诉GPT发送了什么数据:

Python
def Motor_control(user_input):
if "启动" in user_input or "start" in user_input:
command = "1"
ser.write(command.encode())
return "已发送数据1,电机已启动"
elif "停止" in user_input or "stop" in user_input:
command = "0"
ser.write(command.encode())
return "已发送数据0,电机已停止"

效果仍然不理想,原因在于询问时没有给出具体要求。调整问题后,终于得到期望的结果。

5. 编写STM32控制代码

核心功能包括串口通信和PWM输出。以下为主要代码:

C
#include "stm32f10x.h"

void USART1_Init(void);
void TIM1_PWM_Init(void);
void StartMotor(void);
void USART1_IRQHandler(void);

int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART1_Init();
TIM1_PWM_Init();
NVIC_EnableIRQ(USART1_IRQn);
while (1) { }
}

void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

void TIM1_PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 1000;
TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}

void StartMotor(void)
{
TIM1->CCR1 = TIM1->ARR / 2;
}

void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
char receivedData = USART_ReceiveData(USART1);
if (receivedData == '1')
{
StartMotor();
}
}
}

当然,这里只是一个伪代码,具体配置参数还需要根据实际情况调整,但大致流程就是如此。