Skip to content

Commit 3b6aeb0

Browse files
authored
Merge pull request #38 from nicolapiccinelli/simulated_io_port
Adding a dynamic simulation mode into the dVRK
2 parents 779b093 + d1b0ffb commit 3b6aeb0

File tree

9 files changed

+1259
-2
lines changed

9 files changed

+1259
-2
lines changed

Amp1394Config.cmake.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ set (Amp1394_HAS_EMIO "@Amp1394_HAS_EMIO@")
2626

2727
# Whether using curses for console
2828
set (Amp1394Console_HAS_CURSES "@Amp1394Console_HAS_CURSES@")
29+
30+
# Whether building with dynamic simulation
31+
set (Amp1394_HAS_SIM "@Amp1394_HAS_SIM@")

lib/AmpIORevision.h.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ http://www.cisst.org/cisst/license.txt.
3030

3131
#cmakedefine01 Amp1394Console_HAS_CURSES
3232

33+
#cmakedefine01 Amp1394_HAS_SIM
34+
3335
#endif // _AmpIORevision_h

lib/BasePort.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class BasePort
8080

8181
enum { MAX_NODES = 64 }; // maximum number of nodes (IEEE-1394 limit)
8282

83-
enum PortType { PORT_FIREWIRE, PORT_ETH_UDP, PORT_ETH_RAW, PORT_ZYNQ_EMIO };
83+
enum PortType { PORT_FIREWIRE, PORT_ETH_UDP, PORT_ETH_RAW, PORT_ZYNQ_EMIO, PORT_SIMULATION };
8484

8585
// Protocol types:
8686
// PROTOCOL_SEQ_RW sequential (individual) read and write to each board

lib/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ set (SOURCE_FILES
3535
code/EthUdpPort.cpp
3636
code/PortFactory.cpp)
3737

38-
3938
if (Amp1394_HAS_RAW1394)
4039
set (HEADERS ${HEADERS} FirewirePort.h)
4140
set (SOURCE_FILES ${SOURCE_FILES} code/FirewirePort.cpp)
@@ -60,6 +59,13 @@ add_library(Amp1394 STATIC
6059

6160
target_link_libraries(Amp1394 ${Amp1394_EXTRA_LIBRARIES})
6261

62+
option (Amp1394_HAS_SIM "Build Amp1394 with dynamic simulation" OFF)
63+
if (Amp1394_HAS_SIM)
64+
message (STATUS "Building Amp1394 with dynamic simulation")
65+
add_subdirectory(sim)
66+
target_link_libraries(Amp1394 Amp1394Simulation pthread)
67+
endif (Amp1394_HAS_SIM)
68+
6369
option (Amp1394_BUILD_SWIG "Build Amp1394 with Python wrapper" OFF)
6470
if (Amp1394_BUILD_SWIG)
6571
find_package(SWIG REQUIRED)

lib/code/BasePort.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,13 @@ bool BasePort::ParseOptions(const char *arg, PortType &portType, int &portNum, s
615615
ostr << "ParseOptions: failed to find a port number after \"emio:\" in " << arg+3 << std::endl;
616616
return false;
617617
}
618+
else if (arg == std::string("sim"))
619+
{
620+
portType = PORT_SIMULATION;
621+
portNum = 0;
622+
return true;
623+
}
624+
618625
// older default, fw and looking for port number
619626
portType = PORT_FIREWIRE;
620627
// scan port number

lib/code/PortFactory.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ no warranty. The complete license can be found in license.txt and
2727
#if Amp1394_HAS_EMIO
2828
#include "ZynqEmioPort.h"
2929
#endif
30+
#if Amp1394_HAS_SIM
31+
#include "sim/SimulationPort.h"
32+
#endif
33+
3034
#include "EthUdpPort.h"
3135

36+
3237
BasePort * PortFactory(const char * args, std::ostream & debugStream)
3338
{
3439
BasePort * port = 0;
@@ -73,6 +78,14 @@ BasePort * PortFactory(const char * args, std::ostream & debugStream)
7378
#endif
7479
break;
7580

81+
case BasePort::PORT_SIMULATION:
82+
#if Amp1394_HAS_SIM
83+
port = new SimulationPort(-1, debugStream);
84+
#else
85+
debugStream << "PortFactory: Simulation port not available (set Amp1394_HAS_SIM in CMake)" << std::endl;
86+
#endif
87+
break;
88+
7689
default:
7790
debugStream << "PortFactory: Unsupported port type" << std::endl;
7891
break;

lib/sim/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
project(Amp1394SimulationLib)
2+
3+
set(SOURCE_FILES
4+
code/SimulationPort.cpp)
5+
6+
include_directories(
7+
"."
8+
${Amp1394_INCLUDE_DIR}
9+
${Amp1394_EXTRA_INCLUDE_DIR})
10+
11+
# Create Amp1394 simulation library
12+
add_library(Amp1394Simulation ${SOURCE_FILES})
13+
target_link_libraries(Amp1394Simulation ${Amp1394_EXTRA_LIBRARIES})

lib/sim/SimulationPort.h

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#pragma once
2+
3+
#include <vector>
4+
#include <queue>
5+
#include <map>
6+
#include <thread>
7+
#include <atomic>
8+
#include <mutex>
9+
#include <ostream>
10+
11+
#include "BoardIO.h"
12+
#include "BasePort.h"
13+
14+
class SimulationPort : public BasePort
15+
{
16+
public:
17+
SimulationPort(int portNum, std::ostream &ostr = std::cerr);
18+
~SimulationPort();
19+
20+
PortType GetPortType(void) const { return PORT_SIMULATION; }
21+
int NumberOfUsers(void);
22+
bool IsOK(void) { return true; }
23+
unsigned int GetBusGeneration(void) const;
24+
void UpdateBusGeneration(unsigned int gen) {}
25+
26+
unsigned int GetPrefixOffset(MsgType msg) const { return 0; }
27+
unsigned int GetWritePostfixSize(void) const { return 0; }
28+
unsigned int GetReadPostfixSize(void) const { return 0; }
29+
unsigned int GetWriteQuadAlign(void) const { return 0; }
30+
unsigned int GetReadQuadAlign(void) const { return 0; }
31+
unsigned int GetMaxReadDataSize(void) const { return 2048; }
32+
unsigned int GetMaxWriteDataSize(void) const { return 2048; }
33+
34+
bool WriteBroadcastOutput(quadlet_t *buffer, unsigned int size);
35+
bool WriteBroadcastReadRequest(unsigned int seq);
36+
void WaitBroadcastRead(void);
37+
38+
void PromDelay(void) const {}
39+
40+
// Adds board(s)
41+
bool AddBoard(BoardIO *board);
42+
// Removes board
43+
bool RemoveBoard(unsigned char boardId);
44+
45+
protected:
46+
bool Init(void);
47+
void Cleanup(void);
48+
nodeid_t InitNodes(void);
49+
50+
bool ReadQuadletNode(nodeid_t node, nodeaddr_t addr, quadlet_t &data, unsigned char flags = 0);
51+
bool WriteQuadletNode(nodeid_t node, nodeaddr_t addr, quadlet_t data, unsigned char flags = 0);
52+
bool WriteBlockNode(nodeid_t node, nodeaddr_t addr, quadlet_t *wdata, unsigned int nbytes, unsigned char flags = 0);
53+
bool ReadBlockNode(nodeid_t node, nodeaddr_t addr, quadlet_t *rdata, unsigned int nbytes, unsigned char flags = 0);
54+
55+
private:
56+
struct DynamicsParams {
57+
// 2.0e-5 kg m^2 (approx Maxon RE40 rotor + gearhead)
58+
double motor_inertia = 0.001;
59+
// 1.0e-4 Nm/(rad/s)
60+
double viscous_damping = 0.1;
61+
// this must coincide with the NmToAmps scale in the XML config
62+
// where you will find the scale computed as 1/torque_constant
63+
double torque_constant = 0.1;
64+
// this must coincide with the BitsToPosSI scale in the XML config
65+
// where you will find the scale computed as 360/counts_per_turn
66+
double counts_per_turn = 100000.0;
67+
// this is the simulation time step in seconds for joint dynamics
68+
// it should be equal to the period of the control loop or at least
69+
// comparable with the bandwidth of the simulated joint
70+
double dynamics_dt_sec = 0.001;
71+
// let's use multi-step integration for better stability
72+
unsigned int integration_steps = 5;
73+
// Thermal model parameters [C]
74+
double ambient_temp_c = 25.0;
75+
// Reduce heating effect (less aggressive temperature rise) [C/s per A^2]
76+
double thermal_heating_coeff = 0.05;
77+
// Increase cooling (stronger dissipation) [1/s]
78+
double thermal_cooling_coeff = 0.02;
79+
};
80+
81+
struct AxisState
82+
{
83+
// ENC_MIDRANGE
84+
int32_t EncoderPos = 0x800000;
85+
86+
double EncoderVel = 0.0;
87+
double SimPosition = 0.0;
88+
double SimVelocity = 0.0;
89+
90+
int32_t EncoderQtr1 = 0;
91+
int32_t EncoderQtr5 = 0;
92+
int32_t EncoderRun = 0;
93+
94+
// ENC_MIDRANGE
95+
int32_t EncoderPreload = 0x800000;
96+
int32_t EncoderOffset = 0;
97+
98+
// zero current
99+
uint32_t MotorCurrent = 32768;
100+
101+
// Default to OFF
102+
uint32_t MotorStatus = 0x00000000;
103+
104+
// Simple temperature state [C]
105+
double TemperatureC = 25.0;
106+
107+
// Per-axis dynamics parameters
108+
DynamicsParams params;
109+
};
110+
111+
struct BoardState
112+
{
113+
uint32_t Timestamp = 0;
114+
uint32_t Status = 0;
115+
uint32_t DigitalIO = 0;
116+
uint32_t Temperature = 0;
117+
118+
AxisState Axes[4];
119+
120+
// Simulation behavior: require a power-off after startup before granting amp enable
121+
// When true, motors won't transition to STATUS until a power-off event is seen once.
122+
bool RequirePowerCycleLatch = true;
123+
bool HasSeenPowerOff = false;
124+
};
125+
126+
class WriteRequest
127+
{
128+
public:
129+
nodeid_t node;
130+
nodeaddr_t addr;
131+
quadlet_t data;
132+
unsigned char flags;
133+
134+
WriteRequest(nodeid_t n, nodeaddr_t a, quadlet_t d, unsigned char f)
135+
: node(n), addr(a), data(d), flags(f) {}
136+
};
137+
138+
const uint32_t PERIOD_MASK = 0x03FFFFFFu;
139+
const uint32_t DIR_BIT = 0x40000000u;
140+
const uint32_t OVF_BIT = 0x80000000u;
141+
142+
const char kSimQLASN[12] = "QLA 1234-56";
143+
const std::string SimFPGASerialString = "FPGA 1234-56";
144+
145+
// i want a queue to store request for every board id or node id separately
146+
std::map<nodeid_t, std::queue<WriteRequest>> WriteRequestQueues;
147+
std::map<nodeid_t, BoardState> mBoardStates;
148+
149+
// Simulated FPGA PROM state
150+
uint32_t SimPromCurrentAddr;
151+
152+
// Dynamics simulation thread state
153+
std::thread dynamicsThread;
154+
std::mutex stateMutex;
155+
std::atomic<bool> dynamicsRun{false};
156+
157+
DynamicsParams joint_params{};
158+
159+
// Helper to fetch a simulated PROM byte at absolute 24-bit address
160+
uint8_t GetSimPromByte(uint32_t abs_addr) const;
161+
quadlet_t ProcessPROM(nodeid_t node);
162+
163+
// Advance simple closed-loop joint dynamics for all axes
164+
void UpdateAxisDynamics(nodeid_t node, BoardState &state, double dt);
165+
void DynamicsThreadFunc();
166+
};

0 commit comments

Comments
 (0)